星期四, 十月 05, 2006

WINX支持DirectX,OpenCV吗?

偶尔也会听到这样的一些疑问:WINX支持DirectX,OpenCV吗?也会听到SmartWin支持OpenCV这样的说法。下面我们分析一下这个问题。

我们知道,库之间共存的障碍,主要有以下几点:

其一:编译期的符号(指类名、函数名、宏名等)冲突。主要表形在:

  • 宏名冲突(由于没有命名空间的保护)。
  • 基本类型的typedef。有不少库喜欢自己typedef一下所有的基本类型。如uint32, int32等。由于这些类型非常常见,并且typedef发生在全局命名空间,冲突的概率就很大。

其二:链接期的符号冲突。根据我的经验,这主要表现在:

  • 库之间的符号冲突,即两个库同时提供了某个函数。最为典型的是全局new/delete算符的重载C++允许重载全局new/delete算符,这真的是一场灾难。在两个库同时重载了new/delete时,就出现了符号冲突(可能也会在编译期体现,但常见的情况在链接期才表现出来)。
  • 使用了不同模式的C库。如果两个静态库(Static Library)使用了不同模式的C库,那么他们将出现大量的符号冲突。而我们知道,Visual C++提供了6种模式的C库:
      Single-Threaded
      Debug Single-Threaded
      Multithreaded
      Debug Multithreaded
      Multithreaded DLL
      Debug Multithreaded DLL
  • 不同编译器编译的静态库(Static Library)不能共存。原因主要亦在于使用了不同的C库。

其三:框架的假设。一些库是以框架方式提供的,要求用户按照其预定的方式进行调用。如果两个库均提供了框架,那么他们互不知晓的情况下,通常很难在一起工作。

其四:调用的假设。在你调用一个库的代码时,你需要模拟出它需要的环境。可分为两种情形:

  • 显式的依赖。例如,你要调用QT的函数,很多时候,你需要in/out一个QString参数。当然,既然你用了QT,生成一个QString还是很容易。但是如果某个函数要求传入QWidget*指针呢?除非你的窗体(Widget)本来就是QT实现的,不然这个QWidget*的生成还是颇费脑筋。
  • 隐式的依赖。例如,你要调用MFC的一段代码,而该代码使用了AfxGetApp或者其他。那么显然你的程序需要有AfxGetApp。你的本意可能只是需要一个MFC组件,最后你却发现,最终你不得不依赖一个MFC框架(Framework)。又如ATL/WTL的_Module,ATL/WTL本身架构精巧,但是_Module很要命。个人认为它破坏了ATL/WTL本身的纯洁。因为它其实与AfxGetApp并无二致,最终导致了强耦合的结构。

WINX如何解决这些问题?

其一:编译期的符号冲突。WINX使用namepsace尽量减少对全局命名空间的污染。对宏名亦采用类namespace的解决方案,多数WINX的宏名均以“WINX_”开头。

其二:链接期的符号冲突。和STL一样,WINX的代码尽量以纯头文件的方式提供。

其三:框架的假设。WINX不是框架,以便有更好的适应性。

其四:调用的假设。WINX的函数规格尽量减少采用WINX特有的数据结构。另外,类似MFC的AfxGetApp(),WTL的_Module的全局性数据,这在WINX中是明令禁止的(只是由于WINX使用部分WTL的代码,一些时候,_Module的使用不能避免)。

所有这一切,均是为了让WINX在最大程度上和更多的库可以协作。而我们在前面也已经提到了,WINX尽量采用了更为开放的结构。相比之下,它更懂得与其他库一起协作的道理。

星期三, 十月 04, 2006

对比WINX,WTL,MFC,SmartWin代码效率

我们以Hello, World! 程序为例,对比一下各个界面库的代码效率。对于界面程序,个人认为空间效率较之时间效率要占据主导因素,故此这里比较的是空间效率。另外,由于优化的极限是直接用Windows SDK,故此对比亦加入Windows SDK作为参考。参与此次对比的有:

  • WINX
  • WTL
  • MFC
  • SmartWin
  • Windows SDK

功能:Hello, World!

界面:模态对话框

编译器:Visual C++ 2005

源代码:

比较结果:

首先,我们对比一下静态链接多线程模式的C库——即MultiThread(MT)时的情形。MFC亦以静态链接方式链接。由于所有的代码均静态链接进去,这种方式无疑是最公平的。对比结果如下:

  • Windows SDK:48.0 K Reference:kernel32.dll, user32.dll
  • WINX:52.0 K Reference:kernel32.dll, user32.dll
  • WTL:76.0 K Reference:kernel32.dll, user32.dll, advapi32.dll, ole32.dll, oleaut32.dll
  • SmartWin:132.0 K Reference:kernel32.dll, user32.dll, comctl32.dll
  • MFC:184.0 K Reference:kernel32.dll, user32.dll, advapi32.dll, gdi32.dll, oleaut32.dll, shlwapi.dll, winspool.drv

可以看出,WINX产生的代码效率最高,并非常接近Windows SDK,而WTL则次之。SmartWin虽然以模板构建,但是比之MFC并无太大的优势。

我们再来比较一下动态链接多线程模式的C库——即MultiThread DLL(MD)时的情形。MFC采用动态链接方式。这是大型程序典型的链接方式,因此这个比较结果也颇有意义。

  • Windows SDK:6.0 K Reference:kernel32.dll, user32.dll, msvc80.dll
  • WINX:7.0 K Reference:kernel32.dll, user32.dll, msvc80.dll
  • WTL:28.5 K Reference:kernel32.dll, user32.dll, msvc80.dll, advapi32.dll, ole32.dll, oleaut32.dll
  • SmartWin:由于SmartWin编译的lib中没有MultiThread DLL(MD)模式,这里未针对其进行比较。
  • MFC:10.5 K Reference:kernel32.dll, user32.dll, msvc80.dll, mfc80.dll

尽管MFC采用动态链接mfc80.dll的方式,但是它生成的代码仍然不及WINX短小。

星期三, 九月 27, 2006

AOP, Signal/Slot, and Decoupling

解耦(Decoupling)是一个永恒的话题。本来没有打算这么早开始涉及“大型程序解耦”这一块内容,但是smithfox在winxcn论坛上提及相关的话题,所以决定还是在这里聊聊我对“解耦”的一些看法。

面向方面编程(AOP,Aspect Oriented Programming)思想的精粹,在于提倡人们尽量对功能进行切片,形成一个个独立的服务。而后,通过组合的方式,把这些服务组装成为所需要的组件。AOP的关注点在于复杂对象(或系统)的解耦(Decoupling)问题

信号-槽(Signal-Slot)机制,是希望提供一个统一的、可伸缩的方式,来规范组件之间的通讯机制。Signal-Slot的关注点在于组件间的解耦(Decoupling)问题。Delphi、C#、QT、SmartWin(Boost)均提供了Signal-Slot机制。另外,COM的ConnectPoint规范亦属于Signal-Slot范畴。

对于在SmartWin的中将消息(或称为“事件”)归类为一个个Aspect,并且采用Singal-Slot方式提供,你怎么看?WINX的消息机制为什么不采用类似SmartWin的Signal-Slot机制?这个问题可以从以下四个角度来回答。

其一:兼容。我已经说过,WINX的一个基调,是要让现有的MFC用户感到熟悉、感到Happy。所以,我不能够采取MFC用户比较陌生的Signal-Slot来进行消息处理。

其二:效率。SmartWin的消息机制无疑使得窗口对象的尺寸迅速膨胀,并且消息分派的效率大幅降低。

其三:Signal-Slot最主要的关注点是组件间的解耦(Decoupling)。一般情况下,我们主要将其用于两种对象(或多种对象)之间的消息通讯。Delphi、C#、QT在这一点上的度把握的相当好。而SmartWin将Signal-Slot机制应用于窗口自身内部的消息分派,让人有点“杀鸡用牛刀”之感。

其四:从AOP角度看。AOP的关注点是提供服务(功能切片),SmartWin只是将消息归为Aspect,并未提供服务,看起来这一个个Aspect主要是出于实现上重用的考虑,个人认为意义不大。真正AOP思想的贯彻者是ATL/WTL(当然,SmartWin既然支持了所有消息的Signal-Slot,自然也可以实现一个个的“功能切片”,尽管我还没有看到,但这可能是因为我不熟悉SmartWin的缘故)。ATL/WTL的消息分派中的MessageMap Chain机制,使得消息处理可以按功能切片进行分割,并最后可以完美的组装在一起。ATL/WTL中这样的“功能切片”太多了(有点吹牛了,其实不多:-),我们可以随意举几个例子:
  - WTL::CDialogResize (窗口布局,不只用于Dialog的Layout)
  - WTL::CDoubleBufferImpl (支持双缓冲Paint机制)
  - WTL::CThemeImpl (支持XP Theme)
  - ...

接下来我们谈谈WINX中大型程序的解耦(Decoupling)。我曾经在C++程序员的困惑一文中提到这个问题(不过这个问题不是C++程序员所特有的),并且把它作为WINX的一个要解决的目标。我在这方面做过尝试,并获得了一定的成果。但是很抱歉,它离我的期望还有一定的差距,关于这一部分的代码目前并未开放。

最后,我要附带对比一下各种的Signal-Slot实现。

Delphi、C#都是从语法角度来支持Signal-Slot机制,其性能、便利、友好程度,显然都到了最佳(Delphi为了效率,每个Signal只支持一个Slot)。QT虽然基于C++,但是其Signal-Slot机制也是从“半语法的角度”来提供(所以就有了moc预处理)。

根据我的猜想,SmartWin的作者正是觉得QT的做法不太纯洁,而试图提供一个标准C++的解决方案。但是,有两个原因让我觉得SmartWin(或者Boost)的Signal-Slot机制不好:

其一:Singal-Slot是一个通用的解耦机制。它将应用到各种层次的组件,而不会只是用于窗口消息处理。因此,Singal-Slot机制的简洁、易用是很重要的。这让我倾向于QT的Singal-Slot实现(只是相比SmartWin、Boost而言)。

其二:Signal-Slot既然关注于组件间的解耦(Decoupling),我个人倾向于它是一个二进制的规范,而不是C++ 模版定义的规范。原因很简单:我不想假设所有的组件实现者均喜欢C++。

.NET平台和Java平台最大区别在哪里?把你的焦点从C#与Java的比较上脱离开来吧。其实两者最大的区别在于,.NET平台推的是其二进制规范CLR(从COM二进制规范延伸),而Java平台推的是Java语言。微软是聪明的。呃,我把话题扯得远了。

星期一, 九月 25, 2006

新域名winxcn.com预告

http://winxcn.com

网站建设中,敬请关注。

星期日, 九月 24, 2006

WINX与ATL/WTL/MFC的关系,以及跨平台问题

说WINX基于ATL/WTL,其实不是准确的说法。实际上WINX的最核心组件(Windows、Dialog、Control)与WTL没有任何关系。只是WINX的句柄类(CWindow、Gdi句柄如CPen等)、资源类(CMenu等)是WTL的实现。

虽然说从重用角度来讲,WINX确实是:ATL -> WTL -> WINX。但对于WINX的性能是不需要担心的。WINX之所以基于WTL,完全是因为并不希望重复制造轮子。但是WINX的窗口机制是独特的,完全区别于现有各种界面库。对COM的连接点事件(ConnectPoint)更是比ATL好得多,使得C++可以如VB、C#一般方便地接收来自ActiveX控件的事件。

我在开发WINX的时候,参考了众多的界面库,如:MFC、ATL/WTL、QT、wxWidgets、SmartWin++,还有sourceforge上的win32gui、vgui等。

我所遇到的第一个问题,是兼容谁的问题。这里的兼容主要是指使用界面的兼容。这个问题不是谁说了算。看看google趋势:


可以看出,MFC、QT用户占了绝大多数。这就为WINX的开发建立了基调:尽量让MFC、QT用户感到熟悉、感到Happy。

另外,我发现一个值得注意的问题:几乎所有的库均有一个“不良倾向”,就是让自己包罗万象,给用户一个完整的解决方案 —— 网络、界面、自动化、XML、OLE等等。 特别是QT、wxWidgets,这种倾向及其明显。

为了明确我不是万能的,WINX不是万能的,在开发之初,我就给WINX一个规则:接纳现有的库,如果不能够提供得更好,就告诉(建议)用户用什么。如果某个问题有多个卓越的解决方案,那么,用户可以自由选择其中一个。

所以,在WINX中,库的关系是平行的:
 WindowSDK:Gdiplus (GDI+)、MSXML、等等
 Xerces-C
 DirectX
 ATL/WTL
 MFC
 STL
 Boost
 Loki
 ...
 WINX

而不是:
   /--- WindowSDK、Gdiplus、MSXML、DirectX
WINX ---- ATL/WTL
   \--- STL、Boost、Loki、Xerces-C

试图让自己成为用户唯一所见,这是一个危险的想法,我认为是这样。

举个例子,用户希望读取xml文件。那么用MSXML,还是用Xerces-C,还是expat?我不能确定用户想要什么。有一点可以肯定的是,WINX不会开发出另外一个XML Parser。如果WINX的代码需要一个XML Parser,我会尽量在一个比较抽象的层次,为以上三者提供一个共同的界面(当然,不是抽象所有的功能,只是WINX所需要的),就如我们抽象WINX_ASSERT一样。

我们回头看WINX_ASSERT这个例子。WINX中是这么实现的:

#if defined(ASSERT)
#define WINX_ASSERT(e) ASSERT(e)
#elif defined(_ASSERTE)
#define WINX_ASSERT(e) _ASSERTE(e)
#else
#ifdef _DEBUG
#define WINX_ASSERT(e) assert(e)
#else
#define WINX_ASSERT(e) 0
#endif
#endif

这意味着什么?ASSERT来自MFC,_ASSERTE来自MSCRT(参见crtdbg.h)、assert来自C标准库。这里没有检测ATLASSERT,是因为ATLASSERT就是_ASSERTE。

尽管实现ASSERT功能并不复杂(肯定远远简单于实现XML Parser),但是我决定还是不自己去做。原因很简单:我没有打算对它作出改进。

最后我们谈谈跨平台。其实我一直在尝试找到一个方案,可以把平台的差异屏蔽。然而,有一点让我感到不安,因为没有谁可以真的做到平台无关,无论我作出多少努力。我要在Linux平台(甚至更多的平台)提供提供Gdiplus(Mono在做这件事)?DirectX?COM?OLE?还是我去告诉用户,不要用Gdiplus,不要用OLE,不要用COM,喏,这里有一个跨平台的方案,你照着办吧。

我会尝试让WINX跨平台。当然我相信只是最核心部分值得这样做。问题的重心仍然在于,你无权阻止用户喜欢用Gdiplus来开发。所以,跨平台的方案,永远有很多种。

星期一, 九月 18, 2006

winx-1.1(stable version)发布

第一个winx-stable版本发布。

接下来一段时间内不会发布新的版本,主要以修改一些反馈的bug为主。winx发布虽然只有二十几天,但是在内部使用已经有较长时间,应该说功能、接口均相对比较稳定。事实上有更多功能是暂时屏蔽的。本着审慎发布一项功能的原则,那些接口需要进一步商榷,或者属于比较外围的功能,均暂时不对外发布。

下一阶段重点会补充一下文档。

星期四, 九月 14, 2006

感谢“无名”

WINX发布不到一个月,已经有一些热心的朋友在试用,并发现了一个重要bug。为了纪念,为此专门发布一个版本:

http://sourceforge.net/project/shownotes.php?group_id=174954&release_id=447445

感谢不相识的朋友。

星期三, 九月 13, 2006

005 - 窗体属性(Window Property)

摆脱丑陋的MessageMap,你不再需要进行消息映射。这是WINX带给你的第一份惊喜。

你的第二份惊喜,是一种全新(至少对MFC、WTL程序员如此)的编程体验:类Delphi的窗体属性编程。

先看几个例子:

// ---------------------------------------
// 设置对话框背景为灰色

class CHelloDlg : public winx::ModalDialog<CHelloDlg, IDD_HELLO>
{
WINX_BKGND_BRUSH(GRAY_BRUSH);
};

// ---------------------------------------
// 设置对话框背景为一幅位图

class CHelloDlg : public winx::ModalDialog<CHelloDlg, IDD_HELLO>
{
WINX_BKGND_PATTERN(IDB_BKGND);
// 这里IDB_BKGND是位图的资源ID
};

// ---------------------------------------
// 设置对话框快捷键

class CHelloDlg : public winx::ModalDialog<CHelloDlg, IDD_HELLO>
{
WINX_DLG_ACCEL();
WINX_ACCEL(IDR_ACCEL);

WINX_CMDS_BEGIN();
WINX_CMD(ID_HELP_ABOUT, OnCmdAbout);
WINX_CMDS_END();

public:
VOID OnCmdAbout(HWND hWnd)
{
winx::SimpleDialog dlg;
dlg.DoModal(hWnd, IDD_ABOUT);
}
};

我们看到,这种代码风格与MFC、WTL是十分不同的。我称之为基于窗体属性的编程风格。

首先需要指出的是,尽管这里以对话框作为例子,但是这些窗体属性是普适的,可以用于任何窗体。

只要一句WINX_ACCEL(IDR_ACCEL),搞定快捷键(WINX_DLG_ACCEL是打开快捷键功能。只需要顶层窗口调用,不是每个控件都需要。WINX_CMDXXX属于命令分派,与快捷键无关),这在WTL中非常难以办到。我们知道,WTL的快捷键机制是基于PreTranslateMessage的,而在WTL的模态对话框中根本没有PreTranslateMessage消息。所以,为了支持快捷键,你不得不改用非模态对话框。

关于窗体属性的概览性描述,请参考:

http://winxcn.blogspot.com/2006/09/002-winxproperty.html

星期一, 九月 11, 2006

004 - Hello, WINX! - 续

我们再来看看用MFC、WTL、WINX来实现的SDI窗口风格的最简单的Hello程序。

// -----------------------------------------
// MFC的Hello程序

class CMainFrame : public CFrameWnd
{
public:
virtual BOOL PreCreateWindow(CREATESTRUCT& cs)
{
if( !CFrameWnd::PreCreateWindow(cs) )
return FALSE;

cs.dwExStyle &= ~WS_EX_CLIENTEDGE;
cs.lpszClass = AfxRegisterWndClass(
CS_HREDRAWCS_VREDRAWCS_DBLCLKS,
::LoadCursor(NULL, IDC_ARROW),
HBRUSH(COLOR_WINDOW+1),
NULL);

return TRUE;
}

protected:
afx_msg void OnPaint()
{
CPaintDC dc(this);
dc.TextOut(1, 1, _T("Hello, MFC!"));
}
DECLARE_MESSAGE_MAP()
};

BEGIN_MESSAGE_MAP(CMainFrame, CFrameWnd)
ON_WM_PAINT()
END_MESSAGE_MAP()

class CHelloMfc2App : public CWinApp
{
public:
virtual BOOL InitInstance()
{
CMainFrame* pFrame = new CMainFrame;
m_pMainWnd = pFrame;

pFrame->Create(NULL, _T("Hello"));
pFrame->ShowWindow(SW_SHOW);

return TRUE;
}
};

// -----------------------------------------
// WTL的Hello程序

class CHelloMainFrame : public CFrameWindowImpl<CHelloMainFrame>
{
public:
BEGIN_MSG_MAP(CHelloMainFrame)
MESSAGE_HANDLER(WM_PAINT, OnPaint)
CHAIN_MSG_MAP(CFrameWindowImpl<CHelloMainFrame>)
END_MSG_MAP()

LRESULT OnPaint(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/)
{
CPaintDC dc(m_hWnd);
dc.TextOut(1, 1, _T("Hello, WTL!"));
return 0;
}
};

WTL::CAppModule _Module;

int APIENTRY WinMain(HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPSTR lpCmdLine,
int nCmdShow)
{
_Module.Init(NULL, hInstance);
{
CMessageLoop theLoop;
_Module.AddMessageLoop(&theLoop);

CHelloMainFrame wndMain;
wndMain.Create(NULL, NULL, _T("Hello"));
wndMain.ShowWindow(nCmdShow);

theLoop.Run();
}
_Module.Term();
return 0;
}

// -----------------------------------------
// WINX的Hello程序

class CHelloMainFrame : public winx::MainFrame<CHelloMainFrame>
{
WINX_CLASS("CHelloMainFrame");
public:
void OnPaint(HWND hWnd)
{
winx::PaintDC dc(hWnd);
dc.TextOut(1, 1, _T("Hello, WINX!"));
}
};

winx::CAppModule _Module;

int APIENTRY WinMain(HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPSTR lpCmdLine,
int nCmdShow)
{
CAppModuleInit module;

CHelloMainFrame::RegisterClass();
CHelloMainFrame wndMain;
wndMain.Create(NULL, _T("Hello"));

return module.Run();
}
// -----------------------------------------

如果你了解Windows SDK编程,你肯定知道一个典型的Windows窗口程序(SDI),通常包含以下三个步骤:

1)注册窗口类。
2)创建窗口。
3)消息循环:分派并在窗口过程处理相应的消息。

我们看到,MFC把注册窗口类的过程隐含在PreCreateWindow,并尽量弱化窗口类概念。这表现在MFC的AfxRegisterWndClass甚至不允许用户主动指定窗口类的名字。WTL进一步弱化了窗口类概念,用户甚至根本就不需要知道RegisterClass(注册窗口类)这回事。

WINX采取了相反的策略,强调窗口类的概念,并认为用户理解窗口类是重要的。具体表现在:

1)用户需要用WINX_CLASS指定窗口类的名字。
2)用户需要在创建窗口类的第一份实例前,主动RegisterClass

这看似麻烦,但其实它是WINX强调可视化编程的关键,这一点我们在后续文章中将详细解释。

最让你惊异的,也许是WINX没有MessageMap。是的,WINX不需要你指定如何分派消息。并且,你将发现,几乎所有的MFC中的消息,WINX中均有对应,并且两者的消息处理函数原型极其相似,基本都是多了一个HWND hWnd参数。例如MFC的void OnPaint(),到WINX中为void OnPaint(HWND hWnd); MFC中的void OnLButtonDown(UINT uFlags, CPoint pt),到WINX为void OnLButtonDown(HWND hWnd, UINT uFlags, winx::CPoint pt); 等等。关于WINX中的消息分派机制,我们后续将进一步介绍。

最后需要提醒的是,WINX目前没有提供自己的消息循环,直接取自WTL。这个例子你看到WINX的WinMain代码比WTL简洁,但这只是假象。WINX只是对WTL的WinMain进行了一定的包装,没有实质性改进。WTL完全可以提供同样简洁的代码。

004 - Hello, WINX!

提到WINX,总是感觉有太多的背景需要交代,以致于到现在才迎来我们经典的第一课——Hello程序。

一个我们已经提到过的事实是:WTL的很多关键特性在模态对话框中不可用。例如PreTranslateMessage (包括快捷键的支持)、UpdateUI等等。而与WTL对模态对话框的“轻视”相反,WINX极大化的强化模态对话框的能力。为了突出这一点,我们的Hello程序首先从模态对话框开始:

// -----------------------------------------
// MFC的Hello程序

class CHelloMfcDlg : public CDialog
{
public:
enum { IDD = IDD_HELLOMFC_DIALOG };

CHelloMfcDlg(CWnd* pParent = NULL)
: CDialog(CHelloMfcDlg::IDD, pParent) {}
};

class CHelloMfcApp : public CWinApp
{
public:
BOOL InitInstance()
{
CHelloMfcDlg dlg;
dlg.DoModal();
return FALSE;
}
};

CHelloMfcApp theApp;

// -----------------------------------------
// WTL的Hello程序

class CHelloDlg : public ATL::CDialogImpl<CHelloDlg>
{
public:
enum { IDD = IDD_HELLO };

public:
BEGIN_MSG_MAP(CHelloDlg)
COMMAND_RANGE_HANDLER(IDOK, IDNO, OnCloseCmd)
END_MSG_MAP()

LRESULT OnCloseCmd(WORD /*wNotifyCode*/, WORD wID, HWND /*hWndCtl*/, BOOL& /*bHandled*/)
{
::EndDialog(m_hWnd, wID);
return 0;
}
};

WTL::CAppModule _Module;

int APIENTRY WinMain(HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPSTR lpCmdLine,
int nCmdShow)
{
_Module.Init(NULL, hInstance);
{
CHelloDlg dlg;
dlg.DoModal();
}
_Module.Term();
return 0;
}

// -----------------------------------------
// WINX的Hello程序

class CHelloDlg : public winx::ModalDialog<CHelloDlg, IDD_HELLO>
{
};

int APIENTRY WinMain(HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPSTR lpCmdLine,
int nCmdShow)
{
CHelloDlg dlg;
dlg.DoModal();
return 0;
}
// -----------------------------------------

是的,对比三者模态对话框的样例,你至少可以看出两点:

1)MFC框架带给MFC模态对话框的,是累赘。WTL、WINX均不提供框架,你可以按自己的意愿写WinMain中的代码。

2)WTL的代码总是看起来显得笨拙(尽管高效)。而单看对话框代码,MFC、WINX看起来比较简洁,因为他们隐含已经处理了IDCANCEL、IDOK代码。

不过,由于只有WTL写了消息处理代码,其余两者均未处理消息,这个样例对WTL而言并不公平。让我们再来看看三者提供的最原始的SDI窗体。

关于这里的各个例子,你可以在WINX提供的tutorials中找到。它们分别是:

tutorials/winx/step001/hello,mfc
tutorials/winx/step001/hello,wtl
tutorials/winx/step001/hello,winx