使用便携式 Class 库作为 COM 接口
Use Portable Class Library as COM interface
我有 Windows 8.1 和 Windows Phone 8.1 的 C# 可移植 class 库。
我想编写一个使用此库的本机(非托管)C++ Windows 应用程序。
据我所知,最佳做法是将我的库公开为 COM 接口。
这似乎是一个简单的解决方案,但是当我创建便携式 Class 库项目时,复选框 "Make assembly COM visible" 和 "Register for COM interop" 被禁用。 Portable Class Library 是不是不可能?
我尝试过的:
我手动将条目 <RegisterForComInterop>true</RegisterForComInterop>
添加到我的项目中,并且 visual studio 构建了我的 .tlb 文件,没有任何错误。
我将它添加到我的 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 文件看起来还不错 - 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);
我已经试过了
- 将程序集添加到 GAC
- 手动注册程序集 Regasm.exe
一切似乎都很好,但异常仍然是一样的。真的不可能吗?我应该制作一个不可移植的图书馆吗?这将是浪费时间,因为我的图书馆使用 classes from portable...
PCL 库被设计成可移植的,因此它们实现了满足所有目标平台的最大功能集。不幸的是,这不包括 COM(即 Mac OS 上没有 COM,因此具有 COM 依赖项会使 Portable 脱离 PCL)。
RegisterForcomInterop 只解决了一半的问题,你还需要处理ComVisible,这可能是你的TypeInitializationException 的原因。 More info here.
这有点开销,但为了省去一些麻烦,将您的 PCL 包装在实现相同接口的标准 class 库中,只需调用 PCL 方法作为传递通过可能是最好的选择。然后使用标准的 ComVisible 等
这里是一些伪代码来解释,在PCL程序集中:
IMyPclInterface
{
void DoSomeWork();
}
public class MyPclImplementation : IMyPclInterface
{
public void DoSomeWork()
{
....
....
....
}
}
在标准 class 库中,引用您的 PCL 库并且:
public class MyComImplementation : IMyPclInterface
{
MyPclInstance myPclInstance;
public MyComImplementation()
{
myPclInstance = new MyPclInstance();
}
public void DoSomeWork()
{
myPclInstance.DoSomeWork();
}
}
正如 Murray Foxcroft 指出的那样,可以复制 PCL 库,编译为标准 class 库。此外,可以为标准 class 库添加 PCL 依赖项,它会起作用。
但是,您不必这样做。尽管 Visual Studio 没有为您提供 COM 的复选框,您仍然可以按照问题中的指示手动添加属性或手动添加 运行 Regasm.exe smth.dll /tlb
,您将获得一个有效的 COM 库。确实很难调试,但 Hans Passant 留下了一些很好的提示:
The wrappers created by the #import directive turn COM error codes
into C++ exceptions. If you want to diagnose the error instead of just
having the program crash then you have to write try/catch (_com_error&
ex) {} to report it. Change the Debugger Type from Auto to Mixed so
you'll have a much easier time trouble-shooting the C# exception,
TypeInitializationException requires that kind of help. And write a C#
unit test so you get the basic problems out before you try to call it
from C++
我有 Windows 8.1 和 Windows Phone 8.1 的 C# 可移植 class 库。 我想编写一个使用此库的本机(非托管)C++ Windows 应用程序。 据我所知,最佳做法是将我的库公开为 COM 接口。
这似乎是一个简单的解决方案,但是当我创建便携式 Class 库项目时,复选框 "Make assembly COM visible" 和 "Register for COM interop" 被禁用。 Portable Class Library 是不是不可能?
我尝试过的:
我手动将条目
<RegisterForComInterop>true</RegisterForComInterop>
添加到我的项目中,并且 visual studio 构建了我的 .tlb 文件,没有任何错误。我将它添加到我的 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 文件看起来还不错 - 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);
我已经试过了
- 将程序集添加到 GAC
- 手动注册程序集 Regasm.exe
一切似乎都很好,但异常仍然是一样的。真的不可能吗?我应该制作一个不可移植的图书馆吗?这将是浪费时间,因为我的图书馆使用 classes from portable...
PCL 库被设计成可移植的,因此它们实现了满足所有目标平台的最大功能集。不幸的是,这不包括 COM(即 Mac OS 上没有 COM,因此具有 COM 依赖项会使 Portable 脱离 PCL)。
RegisterForcomInterop 只解决了一半的问题,你还需要处理ComVisible,这可能是你的TypeInitializationException 的原因。 More info here.
这有点开销,但为了省去一些麻烦,将您的 PCL 包装在实现相同接口的标准 class 库中,只需调用 PCL 方法作为传递通过可能是最好的选择。然后使用标准的 ComVisible 等
这里是一些伪代码来解释,在PCL程序集中:
IMyPclInterface
{
void DoSomeWork();
}
public class MyPclImplementation : IMyPclInterface
{
public void DoSomeWork()
{
....
....
....
}
}
在标准 class 库中,引用您的 PCL 库并且:
public class MyComImplementation : IMyPclInterface
{
MyPclInstance myPclInstance;
public MyComImplementation()
{
myPclInstance = new MyPclInstance();
}
public void DoSomeWork()
{
myPclInstance.DoSomeWork();
}
}
正如 Murray Foxcroft 指出的那样,可以复制 PCL 库,编译为标准 class 库。此外,可以为标准 class 库添加 PCL 依赖项,它会起作用。
但是,您不必这样做。尽管 Visual Studio 没有为您提供 COM 的复选框,您仍然可以按照问题中的指示手动添加属性或手动添加 运行 Regasm.exe smth.dll /tlb
,您将获得一个有效的 COM 库。确实很难调试,但 Hans Passant 留下了一些很好的提示:
The wrappers created by the #import directive turn COM error codes into C++ exceptions. If you want to diagnose the error instead of just having the program crash then you have to write try/catch (_com_error& ex) {} to report it. Change the Debugger Type from Auto to Mixed so you'll have a much easier time trouble-shooting the C# exception, TypeInitializationException requires that kind of help. And write a C# unit test so you get the basic problems out before you try to call it from C++