当心Invoke埋下的雷

15 篇文章 0 订阅
12 篇文章 0 订阅
【标签】dispatch error; invoke error; COM; 连接点; VTS_VARIANT;


【报错提示】
VS2010-0xC015000F 正在被停用的激活上下文不是最近激活的
VS2012-0x00000001 处有未经处理的异常(在 a.exe 中): 0xC0000005: 执行位置 0x00000001 时发生访问冲突


【应用场景】
主程序通过连接点获得COM组件事件通知,事件激发后回调崩溃。有些函数正常,有些崩溃。
崩溃的函数有个特点,事件接口中的参数类型是VARIANT。


【根本原因】
事件激发后,组件会通过连接点的Dispatch接口调用Invoke函数。
Invoke函数的实现中(COleDispatchImpl::Invoke in mfc100ud.dll),会先把参数拷贝到栈上,然后调用映射的函数。

拷贝参数过程中出现了如下代码(oledisp1.cpp):

case VT_VARIANT:
    *(_STACK_PTR*)pStack = (_STACK_PTR)pArg;
    pStack += sizeof(_STACK_PTR);
    break;
也就是当参数是VARIANT类型的时候,只会把参数指针放到栈上,而不是拷贝整个参数。

如果映射的函数中,对应的参数写成VARIANT,那么调用过程中取值和调用完成后释放栈参数就会出错。因为VARIANT*被当成VARIANT了。
如果这个参数的后面还有参数,也会导致错误。因为VARIANT和VARIANT*所占的字节大小是不一样的。

其实微软也注释了,只是我没有仔细看注释,想当然了。在afxdisp.h中,
#define VTS_VARIANT         "\x0C"      // a 'const VARIANT&' or 'VARIANT*'


【解决方案】
接口不用改,激发回调处不用改(向导生成的要改),接收器要改。

//接收器举例,注意最后一个参数类型VTS_VARIANT,对应的OnCallbackA函数中不能是 VARIANT v 必须是 VARIANT* v
BEGIN_DISPATCH_MAP(CxxView, CView)
    DISP_FUNCTION_ID(CxxView,"CallbackA",1,OnCallbackA,VT_EMPTY,VTS_I4 VTS_VARIANT)
END_DISPATCH_MAP()

void CxxView::OnCallbackA(LONG n, VARIANT* v){
}

//向导生成的代理有bug
激发事件后,调用Invoke的Fire_xx函数,其中Fire_xx函数有bug,必须看一下下面的相关链接。

还有一个最简单的解决方案,不用VARIANT,哈哈。。。


【相关链接】
第一,[in]形式的VARIANT参数,要删除向导生成代码中的&即可。
http://support.microsoft.com/kb/250847/zh-cn
错误: ATL 连接点向导生成的代码的 variant 类型的值参数提供 C4305 警告事件
第二,[out]形式的VARIANT*,链接中有两种解决方案。
http://support.microsoft.com/kb/264985/zh-cn
错误: ATL Connection Point Wizard-Generated 代码与事件 [out] 变量 * 或 [out] 长时间 * 参数提供 C4305 警告


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值