从ComImport类调用函数没有';没有像预期的那样失败

本文关键字:失败 ComImport 调用 函数 | 更新日期: 2023-09-27 18:27:01

我正在尝试验证我试图通过COM使用的类是否按预期工作。不幸的是,它似乎在一个本应失败的电话上成功了:

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到界面的铸造工作正常,这表明导向器是可以的。(并且它不能铸造到其他导向器,所以它不是随机成功的)

但当我调用InitializeFromTemplate时,两个参数都设置为null,它就成功了。文件中说,结果应该是E_POINTER错误:

Return code - Description
E_POINTER - The pPolicyServer and pTemplate parameters cannot be NULL.

为什么我没有看到一个例外呢?

从ComImport类调用函数没有';没有像预期的那样失败

问题是您正在重新定义接口,并且新的定义与原来的不同。

GUID是可以的,但在下面,QueryInterface实现检查GUID,并返回指向实现的指针-这是接口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程序集,我不明白为什么不简单地使用那些生成的互操作类。以下是表现如预期的完整示例:

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);
        }
    }
}

这里解释了使用类与接口类型的问题:使用嵌入式互操作类型