从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.
为什么我没有看到一个例外呢?
问题是您正在重新定义接口,并且新的定义与原来的不同。
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);
}
}
}
这里解释了使用类与接口类型的问题:使用嵌入式互操作类型