问题
I wrote a C# program and library that allow a user to more easily use a particular proprietary simulator's COM interface.
Currently I have to build and distribute a different version of the C# library for each version of the simulator I wish to support. The company that develops the simulator regularly releases new versions, and each new version gets an incremented major version number. As far as I can tell, there is no change to the COM interface between versions (yet) so the calls and behavior are all the same from my perspective. However, if I build my library and installer against simulator version 9.0, and a user has version 10.0 installed, my C# program cannot find the simulator's COM interface. (Even minor upgrades to the simulator result in a major version bump to the COM interface.) So I have to build my library against every supported version of the simulator. That's annoying, I would rather distribute a single library that correctly finds and links to the newest simulator at install time.
It seems there must be a way to do this. I thought this page might describe the method:How to: Wrap Multiple Versions of Type Libraries. But it didn't seem to work for me.
Of course, it's likely I just didn't do it correctly. I followed the directions to get .il files for two versions of the COM interface. 9.0 and 10.0. But I was confused about the next step and couldn't find an example:
Using a text editor, insert a second PrimaryInteropAssemblyAttribute attribute below the attribute added by Tlbimp.exe. Include the major and minor version numbers that represent the second type library version.
I wasn't sure if I should duplicate the assembly block, or just the line inside the block (which has no version information I can see.) So I duplicated the assembly block like this:
enter code here
.assembly Interop.Happ
{
.custom instance void
[mscorlib]System.Runtime.InteropServices.ImportedFromTypeLibAttribute::.ctor(string) = ( 01 00 04 48 61 70 70 00 00 )
.custom instance void
[mscorlib]System.Runtime.InteropServices.PrimaryInteropAssemblyAttribute::.ctor(int32, int32) = ( 01 00 09 00 00 00 00 00 00 00 00 00 )
////////////// [SNIP] ///////////////
.ver 9:0:0:0
}
.assembly Interop.Happ
{
.custom instance void
[mscorlib]System.Runtime.InteropServices.ImportedFromTypeLibAttribute::.ctor(string) = ( 01 00 04 48 61 70 70 00 00 )
.custom instance void
[mscorlib]System.Runtime.InteropServices.PrimaryInteropAssemblyAttribute::.ctor(int32, int32) = ( 01 00 0A 00 00 00 00 00 00 00 00 00 )
////////////// [SNIP] //////////////
.ver 10:0:0:0
}
.module Interop.Happ.dll
This didn't work, but I can't see how my other interpretation would even be possible. Can anyone see what I'm doing wrong? Am I on completely the wrong track, or did I just combine the wrong parts of the .il file?
回答1:
We solve this problem for our product by not using tlbimp.exe
and providing custom wrappers.
Our wrappers provide .NET definitions only for interfaces, ignoring type libraries and component classes. The user must use the Activator API to obtain instances from ProgIDs, which can, of course, become a configuration parameter for his application.
This is manageable because interfaces are immutable therefore we must create a wrapper class only once for each interface, even as implementations evolve.
Example wrapper:
[ComImport, Guid("PUT-GUID-HERE")]
public interface IProdistLogger
{
[DispId(1000)]
string Name { [return: MarshalAs(UnmanagedType.BStr)] get; }
[DispId(1001)]
void LogSimple (long level, [MarshalAs(UnmanagedType.BStr)] string message, object location);
}
[ComImport, Guid("PUT-GUID-HERE")]
public interface IProdistLoggingHierarchy
{
[DispId(1000)]
string Type { [return: MarshalAs(UnmanagedType.BStr)] get; }
[DispId(1001)]
IProdistLogger CreateLogger ([MarshalAs(UnmanagedType.BStr)] string name);
}
[ComImport, Guid("PUT-GUID-HERE")]
public interface IProdistLogging
{
[DispId(1000)]
IProdistLoggingHierarchy CreateHierarchy ([MarshalAs(UnmanagedType.BStr)] string type, object configuration);
}
Example client:
public class Program
{
public static void Main (string[] args)
{
IProdistLogging logging = (IProdistLogging)System.Activator.CreateInstance(Type.GetTypeFromProgID("prodist.logging.Logging.5.4"));
IProdistLoggingHierarchy hierarchy = logging.CreateHierarchy("log4cxx", null);
return;
}
}
来源:https://stackoverflow.com/questions/22460689/allowing-multiple-versions-of-the-same-com-library