从 ComImport class 调用函数没有像预期的那样失败
Calling function from ComImport class doesn't fail as expected
我正在尝试验证我尝试通过 COM 使用的 class 是否按预期工作。不幸的是,它似乎在一个应该失败的调用上成功了:
enum X509CertificateEnrollmentContext
{
ContextUser = 0x1,
ContextMachine = 0x2,
ContextAdministratorForceMachine = 0x3
}
[ComImport(), Guid("884e2045-217d-11da-b2a4-000e7bbb2b09")]
class Cenroll { }
[Guid("728ab35d-217d-11da-b2a4-000e7bbb2b09")]
interface IX509CertificateRequestCmc2
{
void InitializeFromTemplate(
[In] X509CertificateEnrollmentContext Context,
[In] IX509EnrollmentPolicyServer pPolicyServer,
[In] IX509CertificateTemplate pTemplate);
}
static void Main(string[] args)
{
var cr = new Cenroll();
var cmc2 = (IX509CertificateRequestCmc2)cr;
cmc2.InitializeFromTemplate(X509CertificateEnrollmentContext.ContextUser, null, null);
}
从 Cenroll 到界面的转换工作正常,这表明 guids 是正确的。 (并且它无法投射到其他 guid,所以它不是随机成功)
但是当我调用 InitializeFromTemplate
时,两个参数都设置为 null
,它会成功。 documentation 表示结果应该是 E_POINTER
错误:
Return code - Description
E_POINTER - The pPolicyServer and pTemplate parameters cannot be NULL.
为什么我没有看到异常?
问题是你重新声明了接口,新的定义和原来的不一样
Guids 没问题,但在下面,QueryInterface
实现检查 GUID,returns 指向实现的指针——这是接口 vtable,方法地址是相对于这个地址计算的(当一个对方法的调用被编译,方法的偏移量被添加到这个地址以获得实际地址)。
在您的实现中,InitializeFromTemplate
是第一个方法,生成的客户端代码在 vtable 的开头调用该方法。
但是在原来的接口中,InitializeFromTemplate
之前还有56个方法,因为有继承链:
IX509CertificateRequest (25 methods)
|
+-> IX509CertificateRequestPkcs7 (8 methods)
|
+-> IX509CertificateRequestCmc (23 methods)
|
+-> IX509CertificateRequestCmc2
certenroll.dll 中的函数地址遵循此布局,因此当您调用接口中声明的 InitializeFromTemplate
时,您调用的是链中的第一个方法,实际上是 IX509CertificateRequest::Initialize
.
作为实验,如果您在 IX509CertificateRequestCmc2
中的 InitializeFromTemplate
之前添加 56 个虚拟方法,您将正确接收到异常:
[Guid("728ab35d-217d-11da-b2a4-000e7bbb2b09")]
interface IX509CertificateRequestCmc
{
void fn1();
void fn2();
...
void fn56();
void InitializeFromTemplate(...);
}
调用将抛出:CertEnroll::CX509CertificateRequestCmc::InitializeFromTemplate: Invalid pointer 0x80004003 (-2147467261)
当然,解决方案不是添加虚拟方法 :) 您应该使用生成的互操作类型,而不是提供您自己的类型。当您引用 certenroll
程序集时,我不明白您为什么不简单地使用那些生成的互操作 classes。这是按预期运行的完整示例:
using CERTENROLLLib;
namespace comcerttest
{
class Program
{
static void Main(string[] args)
{
// If you are embedding the interop types, note that you must
// remove the `Class` suffix from generated type name in order
// to instantiate it. See link at the bottom for explanation:
var cr = new CX509CertificateRequestCmc();
var cmc2 = (IX509CertificateRequestCmc2)cr;
cmc2.InitializeFromTemplate(X509CertificateEnrollmentContext.ContextUser, null, null);
}
}
}
这里解释了使用 class 与接口类型的问题:
Using embedded interop types
我正在尝试验证我尝试通过 COM 使用的 class 是否按预期工作。不幸的是,它似乎在一个应该失败的调用上成功了:
enum X509CertificateEnrollmentContext
{
ContextUser = 0x1,
ContextMachine = 0x2,
ContextAdministratorForceMachine = 0x3
}
[ComImport(), Guid("884e2045-217d-11da-b2a4-000e7bbb2b09")]
class Cenroll { }
[Guid("728ab35d-217d-11da-b2a4-000e7bbb2b09")]
interface IX509CertificateRequestCmc2
{
void InitializeFromTemplate(
[In] X509CertificateEnrollmentContext Context,
[In] IX509EnrollmentPolicyServer pPolicyServer,
[In] IX509CertificateTemplate pTemplate);
}
static void Main(string[] args)
{
var cr = new Cenroll();
var cmc2 = (IX509CertificateRequestCmc2)cr;
cmc2.InitializeFromTemplate(X509CertificateEnrollmentContext.ContextUser, null, null);
}
从 Cenroll 到界面的转换工作正常,这表明 guids 是正确的。 (并且它无法投射到其他 guid,所以它不是随机成功)
但是当我调用 InitializeFromTemplate
时,两个参数都设置为 null
,它会成功。 documentation 表示结果应该是 E_POINTER
错误:
Return code - Description
E_POINTER - The pPolicyServer and pTemplate parameters cannot be NULL.
为什么我没有看到异常?
问题是你重新声明了接口,新的定义和原来的不一样
Guids 没问题,但在下面,QueryInterface
实现检查 GUID,returns 指向实现的指针——这是接口 vtable,方法地址是相对于这个地址计算的(当一个对方法的调用被编译,方法的偏移量被添加到这个地址以获得实际地址)。
在您的实现中,InitializeFromTemplate
是第一个方法,生成的客户端代码在 vtable 的开头调用该方法。
但是在原来的接口中,InitializeFromTemplate
之前还有56个方法,因为有继承链:
IX509CertificateRequest (25 methods)
|
+-> IX509CertificateRequestPkcs7 (8 methods)
|
+-> IX509CertificateRequestCmc (23 methods)
|
+-> IX509CertificateRequestCmc2
certenroll.dll 中的函数地址遵循此布局,因此当您调用接口中声明的 InitializeFromTemplate
时,您调用的是链中的第一个方法,实际上是 IX509CertificateRequest::Initialize
.
作为实验,如果您在 IX509CertificateRequestCmc2
中的 InitializeFromTemplate
之前添加 56 个虚拟方法,您将正确接收到异常:
[Guid("728ab35d-217d-11da-b2a4-000e7bbb2b09")]
interface IX509CertificateRequestCmc
{
void fn1();
void fn2();
...
void fn56();
void InitializeFromTemplate(...);
}
调用将抛出:CertEnroll::CX509CertificateRequestCmc::InitializeFromTemplate: Invalid pointer 0x80004003 (-2147467261)
当然,解决方案不是添加虚拟方法 :) 您应该使用生成的互操作类型,而不是提供您自己的类型。当您引用 certenroll
程序集时,我不明白您为什么不简单地使用那些生成的互操作 classes。这是按预期运行的完整示例:
using CERTENROLLLib;
namespace comcerttest
{
class Program
{
static void Main(string[] args)
{
// If you are embedding the interop types, note that you must
// remove the `Class` suffix from generated type name in order
// to instantiate it. See link at the bottom for explanation:
var cr = new CX509CertificateRequestCmc();
var cmc2 = (IX509CertificateRequestCmc2)cr;
cmc2.InitializeFromTemplate(X509CertificateEnrollmentContext.ContextUser, null, null);
}
}
}
这里解释了使用 class 与接口类型的问题: Using embedded interop types