COM 之 VARIANT_BOOL,失败的设计

COM 的总体设计是很成功的,但针对 VARIANT_BOOL 而做的设计(约束)则相当失败。当一个程序员为 COM 提供 VARIANT_BOOL 返回值时,如果不给予特殊的认真对待,则极易潜伏下难以察觉却影响深远的 bug。在 VC 中为 COM 提供 VARIANT_BOOL 返回值时,正确的做法应该是这样的,不论内部表示(如本例m_editable)被申明成了什么数据类型: 
STDMETHODIMP CMyObject::get_Editable(VARIANT_BOOL * pval) { 
    ATLASSERT(pval); 
    if(m_editable) { 
        *pval = VARIANT_TRUE; 
    } else { 
        *pval = VARIANT_FALSE; 
    } 
    return S_OK; 
}

一个会出 bug 的例子:

STDMETHODIMP CMyObject::get_Editable(VARIANT_BOOL * pval) { 
    ATLASSERT(pval); 
    *pval = m_editable; 
    return S_OK; 

另一个会出 bug 的例子:

STDMETHODIMP CMyObject::get_Editable(VARIANT_BOOL * pval) { 
    ATLASSERT(pval); 
    *pval = ! m_non_editable; 
    return S_OK; 

所出 bug 通常都直接发生在 COM 的客户端,但仅从客户端入手则很难发现它,这类 bug 的根源就在以上接口函数实现上的疏忽。比如从 VB 使用这个 COM 并访问这个属性,有如下代码:

dim isNonEditable as Boolean 
isNonEditable = not myobject.Editable

isNonEditable 为 True 的可能性 非常非常 大, 有一些按正常逻辑期望得到False的情况,但表现却是True ,下面揭示相关的原因。

原 因是在 COM 规范中,VARIANT_BOOL 数据类型只有两个有效值 VARIANT_TRUE (-1) 和 VARIANT_FALSE (0)。而在 VARIANT_BOOL 的实现层面上,实际是一个 short 整数,当然可以包容除 VARIANT_TRUE 和 VARIANT_FALSE 之外的许多其它取值,并且这些值都可以直接通过 COM 调用不作修改地传递给调用者。几乎任何 COM 调用者也都能够容允 VARIANT_TRUE 和 VARIANT_FALSE 之外的值,而且都会一致地解释为 true (比如 VB)。如果光是上面所说的容错情况表面上合理而不足够引起关注的话,下面描述的不同语言之间的处理差异将值得警惕。

在 C++ 语言中,默认使用 1 表示真值,0 表示假值,当遇到任何非 0 值时都理解为真值,在进行逻辑运算之后如果得到真值则返回 1。逻辑运算与位操作运算分离,逻辑运算进行的是整体性操作而不是按位操作,另外有专用的按位操作运算。

在 VB 语言中,默认使用 -1 表示真值,0 表示假值,当遇到任何非 0 值时都理解为真值,在进行逻辑运算之后如果得到真值则返回原值。逻辑运算和按位运算不分离,进行逻辑运算时本质上进行的是按位运算。

在 C++ 中当要为 VARIANT_BOOL 输出真值时如果不特意指定输出规范约定的 VARIANT_TRUE (-1) ,十有八九会输出缺省的 true (1)。当然在 VB 中得到的也是 1,出于容错,它把这个值理解成了 true。在之后的过程中,如果 VB 不对这个值进行逻辑运算,仅从表面上看则是正确的。一旦在 VB 中进行逻辑运算,把 1 参与到某个按位操作的逻辑运算式中,几乎总是得到一个非 0 的值(取异或可以得到 0),既然有容错,所以 VB 一率把这些结果看作 true 。 所以上面的代码 isNonEditable 为 True 的可能性很大,而且这种错误可以随着其参与的逻辑运算进一步扩散。

下面以整数 x 为例,列出了它在VARIANT_BOOL(COM)、Boolean(VB)、bool(C/C++)情况下所代表的内容以及在不同语言下进行“非”操作之后的内容:

问题发生在VARIANT_BOOL的无效范围中,而C/C++的默认true值正好发生在这个无效范围中。注意上表中进行非操作时在VB和C/C++中的不同情形就可以清楚地看到问题所在了。这就是从 COM 返回 VARIANT_BOOL 频繁导致 bug 的原因,并且这里没有“防呆设计”,将来也不会有相关的“防呆设计”。所以从根本上讲,“在 COM 中为 VARIANT_BOOL 规定了取值规范仅限于 VARIANT_TRUE 和 VARIANT_FALSE,但允许其它值被默认容错处理”的设计是失败的设计。


注:--------------------------by: arthur_yu-------------------------

hr = pIDispatch->Invoke(dispid_getparam,IID_NULL,GetUserDefaultLCID(),DISPATCH_METHOD,&param,&rv,NULL,&iError);  

取rv.boolVal 总是0x0000ffff  即-1,查了好多文章,以上这篇是解释了这个原因。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值