使用可移植类库作为COM接口

时间:2016-08-04 15:53:46

标签: c# c++ visual-studio-2015 com portable-class-library

我有适用于Windows 8.1和Windows Phone 8.1的C#可移植类库。 我想编写一个使用该库的本机(非托管)C ++ Windows应用程序。 据我所知,最佳做法是将我的库公开为COM接口。

这似乎是一个简单的解决方案,但是当我创建可移植类库项目时,复选框“使程序集COM可见”和“注册COM互操作”被禁用。便携式类库是不可能的?

我尝试过:

  1. 我手动将条目<RegisterForComInterop>true</RegisterForComInterop>添加到我的项目中,而visual studio构建了我的.tlb文件,没有任何错误。

  2. 我将它添加到我的C ++项目中:

  3. #include "stdafx.h"
    #import "COM\MyLib.tlb"
    #include<iostream>
    using namespace MyLib;
    using namespace std;
    
    int main()
    {
      CoInitialize(NULL);   //Initialize all COM Components
    
      IPayMeLogicPtr core = IPayMeLogicPtr(_uuidof(ComPayMeLogic));
      LoginStatus res = core->Login("myLogin", "myPassword", false, PayMeSDK::Language::Language_ru, true, "");
    
      cout << res;
      return 0;
    }
    

    生成的.tlh文件似乎很好 - here it is in a gist。 但是当我尝试运行此代码时,它会失败并出现例外情况:

    exception at memory location 0x74A5DAE8 (KernelBase.dll) in PayMeDemo.ComConsoleDemo.exe: 0xE0434352 (0x80131534, 0x00000000, 0x00000000, 0x00000000, 0x73A10000).
    exception at memory location 0x74A5DAE8 in PayMeDemo.ComConsoleDemo.exe:  Microsoft C++ exception: _com_error at location 0x00BBFADC.
    

    似乎它发生在.tli的第四行代码中(_hr失败):

    inline enum LoginStatus IPayMeLogic::Login ( _bstr_t Login, _bstr_t password, VARIANT_BOOL testEnviroment, enum Language locale, VARIANT_BOOL savePassword, _bstr_t applicationToken ) {
      enum LoginStatus _result;
      HRESULT _hr = raw_Login(Login, password, testEnviroment, locale, savePassword, applicationToken, &_result);
      if (FAILED(_hr)) _com_issue_errorex(_hr, this, __uuidof(this));
      return _result;
    }
    

    _hr等于0x80131534 error (TypeInitializationException)

    然而,我无法想象哪种类型可能是错的。登录功能如下所示:

        LoginStatus Login(string login, string password, bool testEnviroment, Language locale, bool savePassword, string applicationToken = null);
    

    我已经尝试

    1. 将程序集添加到GAC
    2. 使用Regasm.exe手动注册程序集
    3. 一切似乎都很好但异常仍然是一样的。真的不可能吗?我应该制作一个不可移植的库吗?这将是浪费时间,因为我的库使用便携式的类......

2 个答案:

答案 0 :(得分:1)

PCL库设计为可移植的,因此它们实现了满足所有目标平台的最大功能集。不幸的是,这不包括COM(即Mac OS上没有COM,因此具有COM依赖性会将Portable从PCL中取出)。

RegisterForcomInterop只解决了一半的问题,你仍然需要处理ComVisible,可能是你的TypeInitializationException的原因。 M ore info here.

这有点开销但是为了节省一些痛苦,将PCL包装在实现相同接口的标准类库中,并且只是调用PCL方法作为传递可能是最好的选择。然后使用标准的ComVisible等

这是一些伪代码,在PCL程序集中解释:

IMyPclInterface
{
     void DoSomeWork();
}

public class MyPclImplementation : IMyPclInterface
{
     public void DoSomeWork()
     {
        ....
        ....
        ....
     }
}

在标准类库中,引用您的PCL库并:

public class MyComImplementation : IMyPclInterface
{
     MyPclInstance myPclInstance;

     public MyComImplementation()
     {
          myPclInstance = new MyPclInstance();
     }

     public void DoSomeWork()
     {
          myPclInstance.DoSomeWork();
     }
}

答案 1 :(得分:0)

正如Murray Foxcroft指出的那样,可以制作一个PCL库的副本,编译为标准类库。此外,可以为标准类库添加PCL依赖项,它可以工作。

但是,你不必这样做。尽管Visual Studio没有为您提供COM复选框,但您仍然可以按照问题中的指示手动添加属性,或者手动运行Regasm.exe smth.dll /tlb,您将获得一个有效的COM库。这很难调试,但Hans Passant留下了一些好的提示:

  

#import指令创建的包装器会转换COM错误代码   进入C ++异常。如果您想诊断错误而不是仅仅   程序崩溃然后你必须写try / catch(_com_error&amp;   ex){}报告。将调试器类型从“自动”更改为“混合”   你可以更轻松地解决C#异常问题,   TypeInitializationException需要这种帮助。并写一个C#   单元测试,以便在尝试调用之前解决基本问题   来自C ++