我试图验证我尝试通过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转换到界面工作,这表明guid正常。 (并且它无法投射到其他guid,所以它并非随机成功)
但是当我调用InitializeFromTemplate
时,两个参数都设置为null
,它就会成功。 documentation表示结果应为E_POINTER
错误:
Return code - Description
E_POINTER - The pPolicyServer and pTemplate parameters cannot be NULL.
那么为什么我没有看到例外?
答案 0 :(得分:3)
问题是您正在重新声明界面,并且新定义与原始定义不同。
Guids是正常的,但是在下面,QueryInterface
实现检查GUID,并返回指向实现的指针 - 这是接口vtable,并且相对于该地址计算方法地址(当编译方法的调用时) ,将该方法的偏移量添加到该地址以获取实际地址。)
在您的实现中,InitializeFromTemplate
是第一种方法,生成的客户端代码在vtable的开头调用该方法。
但是,在原始界面中,InitializeFromTemplate
之前还有56个其他方法,因为有一个继承链:
IX509CertificateRequest (25 methods)
|
+-> IX509CertificateRequestPkcs7 (8 methods)
|
+-> IX509CertificateRequestCmc (23 methods)
|
+-> IX509CertificateRequestCmc2
certenroll.dll中的函数地址遵循此布局,因此当您调用接口中声明的InitializeFromTemplate
时,您将调用链中的第一个实际为IX509CertificateRequest::Initialize
的方法。
作为一项实验,如果您在InitializeFromTemplate
IX509CertificateRequestCmc2
之前添加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);
}
}
}
这里解释了使用类与接口类型的问题: Using embedded interop types