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

本文关键字:COM 接口 类库 可移植 | 更新日期: 2023-09-27 18:00:09

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

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

我尝试过的:

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

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

#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文件看起来很好——这里是要点。但当我尝试运行此代码时,它失败了,只有一个例外:

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手动注册程序集

一切似乎都很好,但例外仍然是一样的。真的不可能吗?我应该做一个不可移植的图书馆吗?这将是浪费时间,因为我的库使用可移植的类。。。

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

PCL库被设计成可移植的,因此它们实现了满足所有目标平台的最大功能集。不幸的是,这排除了COM(即,Mac OS上没有COM,因此具有COM依赖关系会使Portable退出PCL)。

RegisterForcomInterop只解决了一半的问题,您仍然需要处理ComVisible,这可能是您的TypeInitializationException的原因。点击此处了解更多信息。

这是一点开销,但为了节省一些痛苦,将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();
     }
}

Murray Foxcroft指出,可以复制PCL库,将其编译为标准类库。此外,还可以为标准类库添加PCL依赖项,这将起作用。

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

由#import指令创建的包装程序将COM错误代码转换为转换为C++异常。如果您想诊断错误,而不是如果程序崩溃,则必须编写try/catch(_com_error&ex){}进行报告。将调试器类型从"自动"更改为"混合",以便解决C#异常的问题会容易得多,TypeInitializationException需要这种帮助。写一个C#单元测试,这样你就可以在尝试调用它之前解决基本问题来自C++