How do I expose a .netstandard2.0 library with COM for use in VB6?

后端 未结 3 1227
春和景丽
春和景丽 2020-12-11 06:16

I have a dotnet core library, a framework 4.7.2 library and a vb6 application.

I want to write a common library for them all to access and so choose .netstandard2.0<

相关标签:
3条回答
  • 2020-12-11 06:42

    The issue is due to assembly binding resolution that fails when ran from VB6 (IDE or compiled .exe file).

    Here are the steps to solve it:

    • Compile the VB project, for example, let's assume the compiled file is Project1.exe.
    • Copy all .NET assemblies (including x86 and x64 directories, and languages directory if localized version is important) aside the compiled VB6 file
    • Now run Project1.exe, you will get an error like this:

    The error is clearly a mismatch between the version of your assemblies aside the Project1.exe file and the version of referenced assemblies (not references you've created yourself but reference embedded in these assemblies... ). You don't see that when you start a .NET program because resolution is a very complex process that depends on a lot of parameters (and it's not getting any better with .NET Core, Framework, Standard, nugets, etc.).

    To futher check it's a mismatch error, you can also use the Fuslogvw.exe (Assembly Binding Log Viewer) tool from the SDK.

    Now we know it's an assembly version mismatch issue, what you can do is create a file named Project1.exe.config aside Project1.exe and add assembly binding redirects to it.

    The easiest way to configure it is to redirect all possible versions to the ones present in the directory that contains your program, so in your case (and as of today, as all these can evolve...), it would be something like this, possibly for every assembly you reference directly or indirectly:

    <configuration>
      <runtime>
        <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
          ...
          <dependentAssembly>
            <assemblyIdentity name="Microsoft.Extensions.DependencyInjection.Abstractions" publicKeyToken="adb9793829ddae60" />
            <!-- 3.1.2.0 is the version of the assembly you ship -->
            <bindingRedirect oldVersion="0.0.0.0-65535.65535.65535.65535" newVersion="3.1.2.0" />
          </dependentAssembly>
          ...
        </assemblyBinding>
      </runtime>
    </configuration>
    

    Unfortunately, there are many satellite assemblies, and it's a bit tedious to create all redirects with correct information, so I've created a tool that creates a .config file with the redirects configured automatically for all .NET assemblies in a given directory: https://github.com/smourier/BindingRedirectGenerator.

    If you want it to work for the VB6 IDE too, you'll have to use the same procedure in a VB6.exe.config file aside VB6.exe.

    0 讨论(0)
  • 2020-12-11 06:49

    A reminder to myself

    • Use a demo UI to access the original DLL to confirm the call works. ( if you can't get it to work skip to making the App.Config for the unit test project using BindingRedirectGenerator )

    • Add a unit test in the com visible project to confirm the call works.

    • Copy all the dlls created by both projects to the release folder

    For each com visible dll run as Administrator

    c:\windows\microsoft.net\framework\v4.0.30319\regasm /verbose /codebase /tlb:MyLibrary.tlb c:\myproject\releasedlls\MyLibrary.dll
    
    • Install BindingRedirectGenerator to c:\brg say

    • At the command prompt change directory to c:\brg

        BindingRedirectGenerator c:\myproject\releasedlls App.config
      
    • Rename App.config to MyVB6Project.exe.config and copy it to the same folder as MyVB6Project.exe

    Remember to set up the files for the vb6.exe folder if you want to run it in the vb6 ide

    Put the whole process in a script for future use ( I used a .bat)

    Keep an eye on what nuget has put in app.config

    Pay attention to the yellow warnings at build time!

    0 讨论(0)
  • 2020-12-11 07:05

    Regarding the .NET standard, I may be wrong but I think this is not applicable here because the COM interop stuff are at a higher level than the one .NET standard is targeting; we can only talk about either .NET Core or .NET Framework for COM interop.

    If you want to generate a type library, you have few options.

    By far, the easiest method is just to use .NET Framework. The fact that you are wanting to create a type library negates the advantages of .NET Core already because several COM, especially the "Automation" features are Windows-only. Using framework will be fine at least until .NET Core 5 comes out.

    That said, if you have a business reason for using .NET Core but still need COM support, including the type library, then based on this GitHub comment, you should be able to compile your own IDL. Note that requires you to install C++ build tools because the MIDL compiler is not really a standalone thing that you can get without the rest of the C++ build tools.

    It is strongly suggested to have had read the documentation on how .NET Core handles COM activation.

    Assuming having the C++ build tools is not a barrier for you, the steps would be the following:

    1) Create a .idl file that defines all your COM interfaces in the IDL format. That requires some translation between the .NET interface and the COM interface. Here's a partial example of how you'd need to translate between your C# interface and COM interface as defined in IDL:

    [
      Guid("<some gooey>"),
      InterfaceType(ComInterfaceType.InterfaceIsDual)
    ]
    public interface IFoo 
    {
       string Bar { get; }
       string Baz(int Fizz);
    }
    

    Would be translated into IDL:

    [
      uuid(<assembly gooey>),
      version(1.0)
    ]
    library myFoo
    {
      [
        uuid(<some gooey>),
        object,
        dual
      ]
      interface IFoo : IDispatch {
        [propget] HRESULT Bar([out, retval] BSTR* retVal);
        HRESULT Baz([in] long Fizz, [out, retval] BSTR* retVal);
      }
    }
    

    Once you've defined the .idl file and it is an accurate representation, you can then use MIDL to compile the .idl file into a .tlb file. Usually something like midl foo.idl /tlb: foo.tlb. You should make use of the MIDL language reference to help you write the .idl file. As a quick way to get started, you could copy your C# interfaces to a .NET framework project, use tlbexp, then use oleview (available via Visual Studio Developer Command Prompt) or olewoo to view the resulting IDL file to get you started.

    The next step is to then create registry keys so that your CLSID can reference the type library. You will need to have your assembly's GUID handy and it must be used as the library's uuid in the .idl file as well.

    Using IFoo interface example, you would need to create the registry similar to below (using .reg format for easy sharing/comprehension and assuming per-user installation, rather than per-machine):

    Windows Registry Editor Version 5.00
    
    [HKEY_CURRENT_USER\Software\Classes\Interface\{<some gooey>}]
    @="IFoo"
    
    [HKEY_CURRENT_USER\Software\Classes\Interface\{<some gooey>}\ProxyStubClsid32]
    @="{00020424-0000-0000-C000-000000000046}"
    
    [HKEY_CURRENT_USER\Software\Classes\Interface\{<some gooey>}\TypeLib]
    @="{assembly gooey}"
    "Version"="1.0"
    

    You will also need to create the registry in the CLSID, Interface, TypeLib, and Record as needed. This article provides a good overview of all registry keys but keep in mind it's assuming .NET framework, not .NET Core, so not all keys are applicable, especially under the CLSID branch.

    Note that when you run the regsvr32, it will normally create the keys in the CLSID and Interface branches but you will need to add the TypeLib keys under the Interface's branch and also an entry to the TypeLib branch. You also will need to create the ProgId keys, too if you want to support CreateObject functionality.

    Initially, you can start with just a .reg file that you can manually update & maintain but if you have several objects, then it becomes desirable to automate this. This can be also managed via the DllRegisterServer call so that when you execute regsvr32, it will take care of registering the keys. On the other hand, you're now polluting your codebase with registration code. Some elect to use installers to do the registry keys write instead.

    I hope that helps you get started!

    0 讨论(0)
提交回复
热议问题