3

I have an executable called ess.exe. I generate an XmlSerializer DLL for it using sgen.exe, and when I run it, it picks up the generated ess.XmlSerializers.dll and all is well.

I also have a DLL called EntrianSourceSearch_14.dll, which is loaded dynamically using Assembly.LoadFrom(DllPathname). If I generate an XmlSerializer DLL for that DLL, it doesn't pick up the generated DLL; instead it falls back to runtime generation of the XML serialization code.

How do I get my DLL to pick up its corresponding generated XmlSerializer DLL?

Edit: I can't put the XmlSerializer DLL into the executable's directory because my DLL is part of a Visual Studio extension - the executable is Visual Studio (devenv.exe), and copying pieces of my product into its directory would probably be seen as anti-social. :-)

Edit to add more context: This is a Visual Studio add-in and extension. It works with all versions of Visual Studio from 2005 to 2017. The bulk of the code is installed into Program Files, and older versions of Visual Studio simply load it from there as an add-in. For newer versions of Visual Studio, which don't support add-ins, there is an extension which is just a loader for those main components in Program Files. That extension uses LoadFrom to load those main components, one of which is EntrianSourceSearch_14.dll.

RichieHindle
  • 272,464
  • 47
  • 358
  • 399
  • You can see a snippet of the code that loads the assembly in [this post](https://stackoverflow.com/a/3798614/17034). Note the call to Assembly.Load(), it is not going to look in the same directory that LoadFrom() did. You can verify that with Fuslogvw.exe. Consider moving the xmlserializers.dll assembly into the same directory as the exe. – Hans Passant Sep 10 '17 at 19:12
  • @HansPassant: Thanks for the explanation, and the link to the assembly-loading code. Regarding "moving the xmlserializers.dll assembly into the same directory as the exe", the DLL is part of a Visual Studio extension, so the executable is Visual Studio (devenv.exe). Copying pieces of my product into its directory would probably be seen as anti-social! – RichieHindle Sep 10 '17 at 22:49
  • Important details like that belong in the question, not a comment. Add as much contextual info as possible. Then some more. – Hans Passant Sep 10 '17 at 23:02
  • Have you tried to hook `AppDomain.AssemblyResolve` event https://msdn.microsoft.com/en-us/library/system.appdomain.assemblyresolve.aspx see if you can pass the information when/if asked? – Simon Mourier Sep 11 '17 at 10:38
  • the runtime dll and dynamically loaded dll both may be loaded but the order in which they are loaded might be the first place I would look. maybe that is why the runtime is always used? – Aaron. S Sep 11 '17 at 15:43
  • I may have a solution, but it'll involve changing the code that calls `Assembly.LoadFrom` - is that doable for you? If so, try this: Replace `Assembly.LoadFrom` with `Assembly.Load(AssemblyName)` and provide both the `Name` and `CodeBase` properties of `AssemblyName`. Then load your serializers dll using the same technique before you attempt any XML serialization. You may attempt to just get away with calling `LoadFrom` on your serializers dll, but [read this](https://learn.microsoft.com/en-us/dotnet/framework/deployment/best-practices-for-assembly-loading) beforehand. – Lucas Trzesniewski Sep 11 '17 at 23:23
  • 2
    Folks, keep in mind that the OP did a poor job documenting the question. This is a VS extension, it should never use LoadFrom since all binaries must be deployed by the extension installer. But whether this is an add-in or a VSIX extension, what VS version is used is and whether this was just a hack to debug the code is all but a blind guess. – Hans Passant Sep 12 '17 at 14:40
  • @LucasTrzesniewski: Thanks! I tried your suggestion, but no luck. From what I've read, I suspect that any loading method that specifies a pathname ends up in the load-from context, whereas the serialization code always uses the default load context. – RichieHindle Sep 12 '17 at 19:14

1 Answers1

2

If you already build and deploy a signed serialization assembly, you can add a CurrentlDomain.AssemblyResolve handler while you load and cache the serializers, something like:

    private Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args)
    {
        var codeBase = new Uri(Assembly.GetExecutingAssembly().CodeBase).LocalPath;
        var serializerPath = Path.Combine(Path.GetDirectoryName(codeBase), new AssemblyName(args.Name).Name + ".dll");
        if (File.Exists(serializerPath))
            return Assembly.LoadFrom(serializerPath);
        else
            return null;
    }

However, I couldn't find an easy way to include the deployment of the serialization assembly in a single-assembly VS extension.

I did find an alternative method that might be preferable. It avoids calling Assembly.LoadFrom directly and some of the assumptions in the above code:

  • Move the code that uses Xml Serialization to a separate assembly
  • For this assembly:
  • Add a Project reference to the above assembly from the extension package
  • Add a Browse.. reference to the built serialization assembly from the extension package
Peter Wishart
  • 11,600
  • 1
  • 26
  • 45
  • This code works beautifully (with the addition of `+".dll"`) - many thanks! I have one question: why do you say "If you already build and deploy a **signed** serialization assembly" - does signing the assembly make a difference? It all seems to work without... – RichieHindle Sep 12 '17 at 17:52
  • Have edited out that bug :). With VS 15 I got "a strongly named assembly is required" when the SGEN assembly wasn't signed. The build process is probably signing the SGEN assembly for you automatically. – Peter Wishart Sep 12 '17 at 21:36
  • I'm currently generating the assembly by running sgen by hand, so there's no explicit signing going on. Not to worry - I can always sign it anyway, just in case. (If you don't mind, how did that "a strongly named assembly is required" error appear? An exception from Assembly.LoadFrom(), or something else?) – RichieHindle Sep 12 '17 at 21:41
  • I had a handler for `AppDomain.FirstChanceException` (to catch the failed load of the SGEN assembly) which observed that exception *after* the `AssemblyResolve` handler loaded the SGEN assembly successfully. – Peter Wishart Sep 12 '17 at 22:03