How to create isolated/reg-free COM between Visual Basic DLLs and a C++ DLL?

ぃ、小莉子 提交于 2019-12-01 11:14:56
Hans Passant

You are making an oddly common mistake, expecting Windows to solve the chicken-and-egg problem. A brief word about the way manifest works might help.

Windows loads the content of a manifest when it load an executable file, the entries are added to an internal lookup table. Whenever an application first asks to create a COM object, underlying call is CoCreateInstance() which supplies the CLSID guid, it first consults that lookup table. If the CLSID is a match then the entry tells it what DLL must be loaded. If there is no match then it falls back to the traditional registry lookup.

The chicken-and-egg is that your DLL didn't get loaded yet. So its manifest entries are not yet active.

The egg must come first, the <clrClass> entry needs to go into the manifest you embed in the C++ executable. Like this:

  <assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
    <assemblyIdentity type="win32" name="AccConn" version="1.0.0.0" />
    <file name="foobar.dll"/>
    <clrClass ...etc>
    </clrClass>
  </assembly>

MSDN article is here.

You should use resource id #2 for DLLs, so change your execution of mt.exe to use #2 instead of #1. At least it is so for native DLLs, I'm not sure it has to be so for managed DLLs, so try it out.

You should move everything about the VB.NET DLL, except the dependency, to the VB.NET manifest, as that's the right place to keep that information.

You should use clrClass nodes, instead of comClass nodes, for .NET COM classes.

You may need to generate a type library (tlbexp.exe), and declare it in the manifest (optionally, you may also embed it in the DLL, but I advise against this if the DLL itself is big), if you want your classes and interfaces to be inspectable and marshalable by type library consumers, e.g. VBA, VC++ #import directive and code generators in general. On top of this, you may need to declare exported interfaces in the manifest, if you create objects outside VB.NET that implement its exported interfaces and which must me marshaled.

If your library is the sole generator of objects that implement the exported interfaces used in the same apartment, you don't need to generate nor declare a type library. I think .NET implements IMarshal, so this particular scenario works without a type library.


AccConn.manifest:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
  <assemblyIdentity type="win32"
                    name="AccConn"
                    version="1.0.0.0" />
  <dependency>
    <dependentAssembly>
      <assemblyIdentity type="win32"
                        name="AccConnVB"
                        version="1.0.0.0" />
    </dependentAssembly>
  </dependency>
</assembly>

AccConnVB.manifest:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
  <assemblyIdentity type="win32"
                    name="AccConnVB"
                    version="1.0.0.0" />
  <file name="AccConnVB.dll" />
  <clrClass clsid="{70da7ef0-1549-4b27-9b00-ade5f37aecbe}"
            name="AccConnVB.tables"
            progid="AccConnVB.AccConnVB"
            threadingModel="Both" />
</assembly>

EDIT: According to your question's update.

You don't have a dependency element on the C++ manifest, so it doesn't know about the VB.NET assembly COM-wise without registration.

It seems you're declaring the VB.NET type library in the C++ manifest.

It seems you're declaring a comClass in the C++ manifest that's declared as a clrClass in the VB.NET manifest. Don't do this!

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!