Tuesday, July 13

解决无法看MSDN的鬼问题

我遇到奇怪的MSDN内容无法显示问题已经若干个月了。在我安装最新的2004.7版本以后更是完全无法使用MSDN了。我的开发环境是Windows XP Profession SP2+VS.NET 2003。MSDN的所有页面都显示'This page cannot be displayed' ,在我以前安装的msdn版本里还能有个别页面可以显示文字。这回变的更加彻底了。而我在另外一台windows2000的机器上安装这套MSDN则完全没有问题。
我google以后还真找到了难兄难弟http://www.dotnet247.com/247reference/msgs/48/243646.aspx,
"The help wouldn't work at all, I keep getting 'This page cannot be displayed' and the error is 'Cannot find server or DNS Error Internet Explorer', so finally I re-installed MSDN for VS.NET 2003 and i still keep getting the same problem. Even clicking on 'Help on Help' was coming up with the same error"

经过无数次的重装MSDN,uninstall 各种软件都无效后,发现了问题所在。如果我以另外一个account登录windows,msdn的内容就可以正确了。而ms-help协议实际就是安装在自己机器里的http,也是通过IE来访问的。所以判断可能是cache有问题了。
我使用另外一个具有administrator权限的帐号登录,找到C:\Documents and Settings\\Local Settings\Temporary Internet Files\Content.IE5 删除。回到我的account里,MSDN就完全正确了。
具体原因也许是index.dat 莫名其妙地被lock上了。也许就是也许。

参照: http://chuacw.hn.org/chewy/archive/2004/03/25/392.aspx

Wednesday, July 7

揭开native event的面纱
这是我的第3篇关与native event的随笔。第一篇对native event作了简介。第二篇报告一个BUG。这一篇我们看看native event是如何实现的,并且尝试解决我们遇到的BUG。

从VC7(Microsoft Visual C++ .NET (2002) )开始MSVC提供了native event这一机制,试图为C++引入一个实现delegate的办法。一共提供了4个扩展关键子:__event (定义一个event),__raise(触发一个event),__hook(将一个成员函数连到event上),__unhook(去掉指定的函数),和两个作用在类上的特性:event_source(一个事件源),event_receiver(一个事件接受者)。

为什么会提供这么多看上去很丑陋的关键子呢?这些关键子生成了什么样的代码呢?

可以使用下面的编译命令来编译我在第二篇随笔里的代码。

cl /Fx /Fas native_event.cpp

/Fx编译选项会生成一个native_event.mrg.cpp,这是MSVC编译器对特性作了展开以后生成的源代码。/Fas编译选项生成相应的汇编代码。

我们看看native_event.mrg.cpp就足够了解native event的实现方法了。

__event关键子被展开成为一个事件函数链表指针,一个虚构造体,一个模板构造体,3个模板函数,一个名为__RemoveAllEventHandlers的成员函数,一个与event名同名的inline函数。除此以外还有4个辅助结构体。 可见event机制是非常类似宏定义的实现方案。

__hook通过__AddEventHandler模板函数将函数指针追加到函数链表里。__unhook从链表中删除函数指针。

__raise本身没有什么用处。实际调用的事件函数的时候,会循环调用函数链表里的函数。函数链表的每个节点里都存储了对象实体指针(this)和成员函数指针。成员函数指针同void*指针的转换利用了__eventingGetAddr这个灵巧的辅助类。

虽然代码看上去有点乱,但并不是很长,应该很容易看懂。

我在第二篇随笔里提到的bug,发生在

int __isEqual(void* p, void* pfn)
{
return ((T*) p == this->p) && (__eventingGetAddr::__getMAddr(pfn) == (void ( T::*) ()) pmfn);
}
这个函数里。多继承的情况下对(void ( T::*) ()) 形式的指针做比较是比较微妙的,明明相等的指针确返回了false。查看汇编代码发现一共比较了8个byte,两个DWORD PTR,具体为什么这么做我还需要做一些分析。

看上去一个可行的代替方案是:

int __isEqual(void* p, void* pfn)
{
return ((T*) p == this->p) && pfn == __eventingGetAddr::__getVAddr((void (T::*) ()) pmfn));
}
将函数指针转换成void*,然后再做比较。从asm看到这产生了高效的代码。

这个BUG导致的后果就是:无法在ATL/WTL中使用native event。这使的这一机制几乎失去了意义。

警告:使用native event要小心。

建议:在MSVC改正BUG之前不要使用native event。

CodeProject里有一篇最近发表的文章提出一种FastDelegate方法,似乎是不错的single-target delegate 解决方案。boost的function和signals则是更加值得关注的。

Wednesday, July 07, 2004 2:49 PM

BUG Report: Failed When a Native Event is Unhooked with with Multi-Heritance
SYMPTOMS
当在一个使用多继承类作为event_receiver的未托管应用程序中unhook (__unhook) 一个native events时, 将会失败。__unhook将返回1(表明没有成功地unhook。在应用程序结束以后native event机制造成了内存泄漏.
CAUSE
这个问题发生在事件的接受者(event_receiver)是多继承类的情况下。在unhook的时候event的注入代码无法正确比较成员函数指针。
WORKAROUND
这个问题可以使用下面的代码重现:

#include

class A
{
int a;
};

class B
{
int b;
};

[event_source(native)]
class CSource
{
public:
__event void MyEvent(int nValue);
};

[event_receiver(native)]
class CReceiver : public A, public B //多继承
{
public:
void MyHandler1(int nValue) {
printf("MyHandler1 was called with value %d.\n", nValue);
}

void MyHandler2(int nValue) {
printf("MyHandler2 was called with value %d.\n", nValue);
}
};

int main() {
CSource source;
CReceiver receiver;

__unhook(&CSource::MyEvent, &source, &CReceiver::MyHandler2, &receiver);
__hook(&CSource::MyEvent, &source, &CReceiver::MyHandler1, &receiver);
__raise source.MyEvent(1);
__unhook(&CSource::MyEvent, &source, &CReceiver::MyHandler1, &receiver);//失败
__hook(&CSource::MyEvent, &source, &CReceiver::MyHandler2, &receiver);
__raise source.MyEvent(2);
__unhook(&CSource::MyEvent, &source, &CReceiver::MyHandler2, &receiver);//失败

}


运行结果

MyHandler1 was called with value 1.
MyHandler2 was called with value 2.
MyHandler1 was called with value 2.

MyHandler2没有被正确unhook。

The information in this article applies to:

Microsoft Visual C++ .NET (2002)
Microsoft Visual C++ .NET (2003)
Visual C++ 2005 Express Edition Beta
Wednesday, July 07, 2004 11:10 AM