COM接口使用void指针作参数报错,该如何解决?
问题根源在于你的接口标记了oleautomation和dual属性,这类接口要求所有参数/返回值必须是自动化兼容类型,而LPVOID(void*)不属于自动化支持的类型集合,因此通过IDispatch调用时会触发类型不匹配错误。local属性仅支持将void*作为返回值使用,无法解决参数的问题,你可以根据实际需求选择以下方案:
方案1:移除自动化兼容性,改为纯自定义COM接口
如果你的COM服务器仅需要供原生C++客户端调用,不需要支持VB、脚本或.NET晚绑定等自动化场景,可以移除dual和oleautomation属性,让接口仅继承IUnknown:
[ uuid(E62A1CB0-86A7-40AE-AFE4-75562C32A498) ] interface IComServer : IUnknown { HRESULT GetCurrentAssembly([out] LPVOID* ppAssembly); };
这种情况下,客户端必须通过IUnknown::QueryInterface获取该接口,使用早期绑定调用,完全避开自动化类型检查,自然不会出现类型错误。
方案2:转换为自动化兼容类型(保留dual/oleautomation)
如果需要保留自动化兼容性,必须将LPVOID转换为自动化支持的类型,常见的两种处理方式:
2.1 封装为COM接口(推荐)
如果ppAssembly指向的是你自定义的对象,最好为该对象定义一个COM接口,将返回值改为接口指针:
// 先定义Assembly对应的COM接口 [ uuid(XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX) // 生成新的UUID ] interface IAssembly : IUnknown { // 添加操作Assembly的方法,比如获取数据、释放资源等 HRESULT GetData([out] SAFEARRAY(BYTE)* pData); }; // 修改原接口 [ dual, oleautomation, uuid(E62A1CB0-86A7-40AE-AFE4-75562C32A498) ] interface IComServer : IDispatch { HRESULT GetCurrentAssembly([out, retval] IAssembly** ppAssembly); };
这种方式既符合自动化规范,又保证了类型安全,是最推荐的做法。
2.2 用SAFEARRAY传递原始内存
如果LPVOID指向的是原始内存块,可以将内存数据复制到SAFEARRAY(BYTE)中传递:
[ dual, oleautomation, uuid(E62A1CB0-86A7-40AE-AFE4-75562C32A498) ] interface IComServer : IDispatch { HRESULT GetCurrentAssembly([out] SAFEARRAY(BYTE)* pAssemblyData); };
在服务器实现中,你需要将LPVOID指向的内存内容复制到SAFEARRAY;客户端则从SAFEARRAY中提取字节数据,再自行处理。
不推荐的方案:使用VT_VOID类型的VARIANT
虽然可以通过VARIANT的VT_BYREF | VT_VOID类型传递void指针,但这种方式兼容性极差,多数自动化客户端(如脚本、.NET)无法正确处理,且类型不安全,不建议使用。
内容的提问来源于stack exchange,提问作者Stanislav Panferov

