如何在Visual Basic DLL和C ++ DLL之间创建隔离/免注册COM?

时间:2015-01-14 13:45:17

标签: c++ vb.net dll manifest regfreecom

我必须在C ++ DLL中使用VB(COM)DLL。 我想出了如何从C ++ DLL访问VB(COM)DLL,它可以工作。

现在我遇到了必须使用隔离的COM / reg-free COM的问题,因为我无法在每台必须使用的PC上注册DLL。

我想用清单文件来实现这个目标,但是我无法让它工作,我不知道出了什么问题。

我有一个名为AccConnVB.dll的VB DLL,其中包含以下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" />
<clrClass
        clsid="{70da7ef0-1549-4b27-9b00-ade5f37aecbe}"
        progid="AccConnVB.AccConnVB"
        threadingModel="Both"
        name="AccConnVB.tables" >
</clrClass>
</assembly>

一个名为AccConn.dll的C ++ DLL,带有以下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>

我的C ++ DLL #define s _WIN32_DCOM在其stdafx.h中,#import是AccConnVB.tlb与no_namespace

以下是来自C ++ DLL的方法:

JNIEXPORT jint JNICALL Java_natives_AccessConnection_refreshImportZwei
(JNIEnv *env, jclass jobj, jstring jDatabase){
  jint result;
  CComBSTR database;

  const char* nativeDatabase = env->GetStringUTFChars(jDatabase,0);
  database.Append((LPCSTR) nativeDatabase);

  CoInitializeEx(NULL,COINIT_MULTITHREADED);
  {
      ITablesPtr ptr;
      HRESULT hr = ptr.CreateInstance(__uuidof(tables));
      if (SUCCEEDED(hr))
      {
          result = (jint) ptr->refreshImportZwei(BSTR(database));
      }
  }
  CoUninitialize();
}

我确保所有内容都适用于已注册的AccConnVB.dll,但在未注册的计算机上使用它会失败。

通过在cmd.exe中执行mt.exe来嵌入清单文件,其中包含以下行:mt -manifest H:\AccConnVB.manifest -outputresource:H:\AccConnVB.dll;#1,分别用于AccConn.dll和AccConn.manifest。

在访问AccConn.dll时,没有设置任何其他内容,AccConnVB.dll,AccConn.manifest和AccConnVB.manifest位于同一个文件夹中。

我按照演练here进行了尝试,尝试了一些变体,但没有任何效果。

非常感谢大家!

附件1

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" />
<file name="AccConnVB.dll">
<comClass
        clsid="{70da7ef0-1549-4b27-9b00-ade5f37aecbe}" 
    tlbid="{1CA12FB4-4A5C-41FF-A508-DCC6CE0D26CD}"
    progid="AccConnVB.tables" />
<typelib
    tlbid="{1CA12FB4-4A5C-41FF-A508-DCC6CE0D26CD}"
    version="1.0" helpdir="" />
</file>
<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" />
</assembly>

附件2

OfficeConn.manifest - C ++ - DLL - (更改名称):

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
<file name="OfficeConn.dll" hashalg="SHA1">
    <comClass clsid="{2C0D73B5-7AA4-4D17-970D-042804E206B2}" tlbid="{DB27F83B-DD8E-4AD8-A6A3-9232A9C1692C}">
    </comClass>
    <typelib tlbid="{DB27F83B-DD8E-4AD8-A6A3-9232A9C1692C}" version="1.0" helpdir="" flags="HASDISKIMAGE">
    </typelib>
</file>
<comInterfaceExternalProxyStub name="IOffice" iid="{19485BDA-0BAE-3527-8F9B-C90B43746B03}" tlbid="{DB27F83B-DD8E-4AD8-A6A3-9232A9C1692C}" proxyStubClsid32="{00020424-0000-0000-C000-000000000046}">
</comInterfaceExternalProxyStub>
<comInterfaceExternalProxyStub name="_offClass" iid="{1FA5D7FC-1CAE-49E0-A99E-B97E8FE3466E}" tlbid="{DB27F83B-DD8E-4AD8-A6A3-9232A9C1692C}" proxyStubClsid32="{00020424-0000-0000-C000-000000000046}">
</comInterfaceExternalProxyStub>
</assembly>

OfficeConnVB.manifest - VB-DLL - (更改名称):

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
 <assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
<assemblyIdentity name="OfficeConnVB" version="1.0.0.0" publicKeyToken="38d072ba2818144d" processorArchitecture="msil">
</assemblyIdentity>
<clrClass clsid="{2c0d73b5-7aa4-4d17-970d-042804e206b2}" progid="OfficeConnVB.offClass" threadingModel="Both" name="OfficeConnVB.offClass" runtimeVersion="">
</clrClass>
<clrSurrogate clsid="{453B8C28-201B-3705-8CF1-C492C7B259EA}" name="Microsoft.Office.Interop.Outlook.OlDefaultFolders">
</clrSurrogate>
<clrSurrogate clsid="{B5181856-6837-3E65-AF7B-5020DD408339}" name="Microsoft.Office.Interop.Outlook.OlItemType">
</clrSurrogate>
<clrSurrogate clsid="{ECE70AEA-B928-3392-AE59-01373B29D3DA}" name="Microsoft.Office.Interop.Outlook.OlImportance">
</clrSurrogate>
<clrSurrogate clsid="{D74B5B88-8D75-3D21-A9BA-F6DBDC905F75}" name="Microsoft.Office.Interop.Word.WdSaveOptions">
</clrSurrogate>
<file name="OfficeConnVB.dll" hashalg="SHA1">
</file>
</assembly>

2 个答案:

答案 0 :(得分:3)

你正在犯一个奇怪的错误,期待Windows解决鸡与蛋的问题。关于清单工作方式的简短说明可能有所帮助。

Windows在加载可执行文件时加载清单的内容,这些条目被添加到内部查找表中。每当应用程序首先要求创建COM对象时,底层调用是提供CLSID guid的CoCreateInstance(),它首先会查询该查找表。如果CLSID是匹配的,那么该条目告诉它必须加载什么DLL。如果没有匹配,那么它将回退到传统的注册表查找。

鸡蛋和鸡蛋是你的DLL还没有加载。所以它的清单条目还没有激活。

鸡蛋必须先出现,<clrClass>条目需要进入嵌入C ++可执行文件的清单中。像这样:

  <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文章is here

答案 1 :(得分:1)

您应该将资源ID#2用于DLL,因此将mt.exe的执行更改为使用#2而不是#1。至少对于本机DLL来说是这样,我不确定托管DLL必须如此,所以试试吧。

您应该将VB.NET DLL的所有内容(除了依赖项)移动到VB.NET清单之外,因为这是保存该信息的正确位置。

对于.NET COM类,您应该使用clrClass节点而不是comClass节点。

您可能需要生成一个类型库(tlbexp.exe),并在清单中声明它(可选地,您也可以将其嵌入到DLL中,但如果DLL本身很大,我建议不要这样做),如果你希望您的类和接口可由类型库使用者检查和编组,例如一般来说,VBA,VC ++ #import指令和代码生成器。除此之外,如果您在VB.NET之外创建实现其导出接口的对象,那么您可能需要在清单中声明导出的接口,并且必须将其封送。

如果您的库是实现同一公寓中使用的导出接口的唯一对象生成器,则无需生成或声明类型库。 我认为.NET实现了IMarshal,所以这个特定场景在没有类型库的情况下工作。


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>

编辑:根据你的问题更新。

您在C ++清单上没有依赖元素,因此如果没有注册,它就不会知道VB.NET程序集。

您似乎在C ++清单中声明了VB.NET类型库。

似乎你在C ++清单中声明了comClass,它在VB.NET清单中声明为clrClass。不要这样做!