在编程社区中,共享源代码似乎是一种普遍的实践,它为所有程序员提供了便利。然而,这种便利背后隐藏着一个巨大的风险,那就是“复制粘贴”。尽管复制粘贴可能是使用公开源代码时遇到的最大危险之一,但本文将探讨其背后的真正原因,并提供一些避免这种风险的建议。
作为一名开发编程工具的程序员,在学习如何使用Windows WIN32 API完成许多任务方面已经花费了超过十五年的时间。在编写图形引擎的一部分时,决定开始使用DIB Sections(设备无关位图),因为它们自Windows 95以来就得到了支持,并且为编写低级图形操作引擎提供了强大的工具。使用DIBs似乎足够简单,可能会认为。只需使用CreateDIBSection API函数即可。没有使用任何文件映射,只是让Windows在内存中创建DIB并返回一个DIB句柄。
但是,当然,开始在网上搜索一些代码示例,但原因可能不是想象的那样。在编写编程工具时有一个规则,特别是GUI框架是“永远不要使用在互联网上或书中找到的任何代码,是说永远不要”。相反,会寻找代码示例,只是为了看看它是如何访问WIN32的不同API的。会在纸上写下每个API调用的列表,然后乐趣就开始了。会研究MSDN SDK文档中使用的每个WIN32 API,并尝试完全理解每个API函数的每个方面,记录下对编码任务至关重要的任何内容。然后从头开始,不查看学到的任何代码示例,会完全编写例程,边写边测试其正常功能(即,检查WIN32调用的返回值)。会充分测试代码,确保它正常工作,然后才在工具中使用它。
这种方法的有趣之处在于,因为不复制代码,而是在研究WIN32文档的同时从头开始编写代码,经常发现在网上看到的代码中使用了令人困惑的API调用。在CreateDIBSection的情况下,第一个代码示例之一是在一个非常信任的编程论坛上找到的。使用CreateDIBSection的例子并不多,但那些被发布的例子是由那个论坛社区信任的非常非常有经验的程序员发布的。代码并不是许多人熟悉的典型WIN32风格C代码,而是使用的编程语言。但奇怪的是,代码将CreateDIBSection的返回值处理为全局内存句柄,而不是正常的位图句柄(使用DeleteObject删除),并且代码在DIB句柄上使用了全局内存API。这对来说没有意义,因为从API文档中读到的内容,得出的结论是代码是错误的。按照在API文档中读到的内容正确编写了代码。在代码中的每个API函数上检查是缓慢的,但经常这样做,并遵循API文档中的指导。
让感到惊讶的是,这样一个在编程论坛上享有盛誉的程序员怎么会犯这样的错误?
决定更广泛地搜索互联网上创建和使用DIBSections的代码,超越了使用的编程语言(一种BASIC形式),并开始进行比较。大多数示例都是用C编写的,令人惊讶的是,C示例经常犯同样的错误。当查看BASIC版本时,它几乎与C版本相同(当然,除了语法)。看来程序员,像大多数人一样,也在互联网上搜索示例来学习,并简单地将一个例程从C移植到BASIC,而没有检查原始的C版本是否有任何错误。认为问题是,如果一个程序员,像一样,以前没有使用过API,他们可能对自己不确定,只是被诱惑去复制别人写的代码,认为他们可能比更了解这个是如何工作的。幸运的是,没有这样做,并发现错误存在于在互联网上找到的许多C示例以及开始的BASIC中。但是,是如何在使用以前没有使用过的API时发现错误的呢?
关键是在使用它们之前完全理解API。
花时间仔细阅读API文档。编写了自己的代码,并进行了彻底的测试,仔细检查API调用的返回值。WIN32的一个美妙之处在于,许多函数返回一个通过/失败值,所以可以说调用工作了。遗憾的是,一些程序员没有花时间测试这些返回值。不需要在最终代码中保留测试代码(测试返回值),但在最初测试代码时应该使用它,以确保它正常工作。
WIN32 API有一个长期存在的问题。每个版本的Windows都容忍了许多调用API函数时犯的错误。上面提到的DIBSection代码中的坏代码被Windows容忍了,但这并不意味着它不是坏的。调用WIN32 API时的一些错误不会向用户(甚至开发者)生成任何标志(除了可能的调用的通过/失败返回值)。没有GPF,没有崩溃的程序,它看起来可以工作。这就是危险所在。坏代码可能被容忍,没有立即的反弹,但它仍然存在。然而,一些错误可能会在累积时(同一错误的多次迭代)使Windows崩溃。较新版本的Windows可能不会容忍相同的错误,这解释了“它在Windows XP中工作得很好,但当切换到Windows 7时崩溃了”的体验类型。错误就是错误,不管它们是否被操作系统容忍。