Windows API技术的演化

来源:互联网 发布:逆战显示网络连接断开 编辑:程序博客网 时间:2024/06/04 19:48

概述

从Windows 1.0 到Windows 10,对于普通用户来说,显而易见地,用户界面已经发生了很多变化。


对于Windows的开发者来说,伴随着Windows系统的不断发展,API技术演化也已经在不知不觉经历了好几代的变化。

演化

为了便于比较和讨论,本文将API技术分为5代。

第一代:C风格的API(Win32API)

Windows主要是用C语言开发的,第一代的API使用C风格也是意料之中。这一类API看上去简单高效,但由于缺少封装,参数往往过于复杂。比如说,创建窗口的函数CreateWindow,要填写11个参数。在没有代码智能提示的时代,要记住每个函数的参数也是醉了。


第二代:C++Class Library (MFC)

面向对象风潮来袭,微软用C++对Win32 API进行了一次改头换面的“装修”,创建了Windows平台开发的基础类库,也就是后来著名的MFC。从此,程序员们告别了创建窗口需要11个参数的日子,程序有 “对象” 了,世界也美好了。但是,由于C++缺少一个二进制级别的标准,却给C++类库风格的API带来了意想不到的隐患(我将会在后续的小实验中做展示)。

第三代:COM(DirectX,Office)

COM通过分离接口和实现弥补了C++没有二进制级别标准的不足,确保了模块的稳定性。它的首次应用是OLE2.0 。接口化编程和引用计数的思想对当时面向对象的C++程序员来说是个思维上的冲击,这让当时的开发者们有些不太适应(COM本质论的作者DON当时也认为使用COM是一个 “噩梦” ,直到他顿悟了COM的精髓,写下COM本质论)。另外,由于微软当时急于推出OLE2.0,导致COM文档的缺乏,这给COM的使用和推广带来了不利。

第四代:.NET(WindowsForm, WPF)

.NET的出现,开启了Windows开发的托管语言时代。从API的角度来说,它的稳定性和易用性上比前三代API都要出色,但它有一个硬伤---性能。虽说随着硬件的发展,相比于.NET所带来的开发效率, 它所带来的性能损耗似乎 “情有可原” 。但在移动开发时代,使用.NET作为基础API所带来的性能损耗就不容小觑了。

第五代:WindowsRuntime API

在前四代API的基础上,WindowsRuntime在稳定性,易用性和性能三者中找到了一个新的平衡点。它本身建立在COM之上,遗传了COM的性能优势;受.NET的启发,WindowsMetadata技术大大简化了 COM使用的复杂性,使得WindowsRuntime类的使用如普通的.NET类一样简单,在C#中使用WINRT的类,几乎看不出差别。

实验

下面我们做一个小实验,体验一下5代API的演化过程。

假设现在有这样一个需求:查找给定字符串在文本中的位置,并返回文本的总长度。

C风格API设计如下:

int FindInString(charconst * pcText,charconst * pcFind,int * piLength);


一个如此简单的功能,就需要3个参数,如果功能再复杂一些,比如设置查找选项(是否忽略大小写),那就只能通过添加新的参数了。

为了便于使用,我们将它封装为C++的类库

class __declspec(dllexport)StringFinder{       char const *  m_psz;public:       StringFinder(const char * psz);       ~StringFinder();       int Length(void)const;       int Find(constchar * psz)const;};


看上去好多了,把这个API命名为SF1.0。

假设Length函数的实现是每次都调用strlen方法去计算字符串的长度。由于查找的文本是不变的,从性能优化的角度,我们只需计算一次长度,并将结果缓存在一个私有成员变量中。由于是私有成员变量,对原来的公开API并并不会造成破坏。所以开发人员果断而又快速地修改了代码:

class __declspec(dllexport)StringFinder{       int m_length;       char const *  m_psz;public:       StringFinder(const char * psz);       ~StringFinder();       int Length(void)const;       int Find(constchar * psz)const;};

在编译和测试完成后,把这个版本命名为SF1.1,开发者将老的DLL替换为新的DLL以提高程序性能。但当用户运行程序后,尴尬的事情发生了。。。


由于添加了新的成员变量,老的成员变量在内存中的位置已经产生了偏移,由于原先的程序依然使用老的偏移量来寻址,导致了错误的发生。最后,开发人员只能将新的DLL命名为SF1.1.DLL来和原先的SF.DLL进行区分(这也是MFC解决版本问题的方式)。这样,每个版本的API都通过版本名称被相应地加载进去,虽然看上去有点糟糕,但至少勉强解决了问题。

反思根本原因,是因为在C++类库版本的API中暴露了实现类StringFinder。那么有没有办法不暴露实现类而又能对功能进行封装呢?为了解决这个问题,我将StringFinder类拆封为接口实现两个部分。

// public interfacestruct IStringFinder {       virtual int Length(void)const = 0;       virtual int Find(constchar * psz)const = 0;}; __declspec(dllexport)void CreateStringFinder(       IStringFinder ** finder,       const char * psz);// implement classclass StringFinder :publicIStringFinder{       int m_length;       char const *  m_psz;public:       StringFinder(const char * psz);       ~StringFinder();       int Length(void)const;       int Find(constchar * psz)const;};


由于实现类StringFinder不对外暴露,所以类内部的实现对于调用者来说是透明的,这就是COM接口的基本思想。

C++没有二进制标准的遗留的问题解决了。但是从API的使用者角度来说,COM这种接口式编程和引用计数的方式比较繁琐,影响开发效率。有没有一种让API的稳定性和生产力兼备的API技术呢?

让我们来看一下.NET的实现方式:

 

   public classStringFinder    {        private readonlystring text;         StringFinder(string t)        {            text = t;        }         public int Length {get { … } }         public int Find(string find)        {            …;        }}

这种实现方式中,API的安全性和开发效率上都有所提高,但在程序的性能却要逊色于前3代的API技术

最后,让我们使用WindowsRuntime (CX)来进行实现:

namespace WRCStringFinder{    public refclassStringFindersealed    {              Platform::String^ m_text;    public:              StringFinder(Platform::String^ text);               int Length();               int Find(Platform::String^ find);    };}


现在,我们甚至可以在.NET, C++和JavaScript三个语言平台上调用这套API,并且具备C++的性能和.NET的效率

小结

上面简单讨论了WindowsAPI技术演化过程,个人理解,欢迎拍砖。每种技术都有其各自出现的背景和要解决的问题,不能说那种绝对好,哪种绝对不好。

在接下来的文章中,我会具体介绍Windows Runtime的基本原理和使用实践。

0 0
原创粉丝点击