简体   繁体   English

程序是否应检查“不应该”的WinAPI函数是否失败,但可能会失败?

[英]Should programs check for failure on WinAPI functions that “shouldn't”, but can, fail?

Recently I was updating some code used to take screenshots using the GetWindowDC -> CreateCompatibleDC -> CreateCompatibleBitmap -> SelectObject -> BitBlt -> GetDIBits series of WinAPI functions. 最近我使用GetWindowDC - > CreateCompatibleDC - > CreateCompatibleBitmap - > SelectObject - > BitBlt - > GetDIBits系列WinAPI函数更新了一些用于截屏的代码。 Now I check all those for failure because they can and sometimes do fail. 现在我检查所有那些失败因为他们可以而且有时会失败。 But then I have to perform cleanup by deleting the created bitmap, deleting the created dc, and releasing the window dc. 但是我必须通过删除创建的位图,删除创建的直流并释放窗口dc来执行清理。 In any example I've seen -- even on MSDN -- the related functions (DeleteObject, DeleteDC< ReleaseDC) aren't checked for failure, presumably because if they were retrieved/created OK, they will always be deleted/released OK. 在我见过的任何例子中 - 即使在MSDN上 - 也没有检查相关函数(DeleteObject,DeleteDC <ReleaseDC)是否失败,大概是因为如果它们被检索/创建好,它们将永远被删除/释放OK。 But, they still can fail. 但是,他们仍然失败。

That's just one noteable example since the calls are all right next to each other. 这只是一个值得注意的例子,因为呼叫完全相邻。 But occasionally there are other functions that can fail but in practice never do. 但偶尔还有其他功能可能会失败,但在实践中永远不会。 Such as GetCursorPos. 比如GetCursorPos。 Or functions that can fail only if passed invalid data, such as FileTimeToSytemTime. 或者仅在传递无效数据时才会失败的函数,例如FileTimeToSytemTime。

So, is it good-practice to check ALL functions that can fail for failure? 那么,检查可能因失败而失败的所有功能是否良好? Or are some OK not to check? 或者有些可以不检查? And as a corollary, when checking these should-never-fail functions for failure, what is proper? 作为必然结果,当检查这些应该永不失败的失败函数时,什么是正确的? Throwing a runtime exception, using an assert, something else? 抛出运行时异常,使用断言,还有什么?

The question whether to test or not depends on what you would do if it failed. 是否测试的问题取决于失败时你会做什么。 Most samples exit once cleanup is finished, so verifying proper clean up serves no purpose, the program is exiting in either case. 一旦清理完成,大多数样品都会退出,因此验证正确的清理是没有用的,程序在任何一种情况下都会退出。

Not checking something like GetCursorPos could lead to bugs, but depending on the code required to avoid this determines whether you should check or not. 不检查像GetCursorPos这样的东西可能会导致错误,但是根据避免这种情况所需的代码决定了你是否应该检查。 If checking it would add 3 lines around all your calls then you are likely better off to take the risk. 如果检查它会在你的所有电话周围添加3行,那么你可能最好承担风险。 However if you have a macro setup to handle it then it wouldn't hurt to add that macro just in case. 但是,如果你有一个宏设置来处理它,那么添加该宏以防万一。

FileTimeToSystemTime being checked depends on what you are passing into it. 正在检查FileTimeToSystemTime取决于您传入的内容。 A file time from the system? 来自系统的文件时间? probably safe to ignore it. 可能安全地忽略它。 A custom string built from user input? 从用户输入构建的自定义字符串? probably better to make sure. 可能更好地确保。

Yes. 是。 You never know when a promised service will surprise by not working. 你永远不知道承诺的服务什么时候会因为不工作而感到惊讶。 Best to report an error even for the surprises. 最好报告错误,即使是惊喜。 Otherwise you will find yourself with a customer saying your application doesn't work, and the reason will be a complete mystery; 否则你会发现自己的客户说你的申请不起作用,原因将是一个完全的谜。 you won't be able to respond in a timely, useful way to your customer and you both lose. 您将无法以及时,有效的方式回应您的客户并且您失败了。

If you organize your code to always do such checks, it isn't that hard to add the next check to that next API you call. 如果您将代码组织起来以便始终进行此类检查,那么将下一个检查添加到您调用的下一个API并不困难。

It's funny that you mention GetCursorPos since that fails on Wow64 processes when the address passed is >2Gb. 你提到GetCursorPos很有趣,因为当传递的地址大于2Gb时Wow64进程失败了。 It fails every time. 它每次都失败了。 The bug was fixed in Windows 7. 该错误已在Windows 7中修复。

So, yes, I think it's wise to check for errors even when you don't expect them. 所以,是的,我认为即使你不指望错误也要检查错误。

Yes, you need to check, but if you're using C++ you can take advantage of RAII and leave cleanup to the various resources that you are using. 是的,您需要检查,但如果您使用的是C ++,则可以利用RAII并将清理保留到您正在使用的各种资源中。

The alternative would be to have a jumble of if-else statements, and that's really ugly and error-prone. 另一种方法是混杂使用if-else语句,这真的很丑陋且容易出错。

The longer I've coded with WinAPIs with C++ and to a lesser extent PInvoke and C#, the more I've gone about it this way: 我使用C ++编写WinAPIs的时间越长,PInvoke和C#的编程越小,我就越用这种方式编写代码:

  1. Design the usage to assume it will fail (eventually) regardless of what the documentation seems to imply 设计用法以假设它将失败(最终),无论文档似乎暗示什么
  2. Make sure to know the return value indication for pass/fail, as sometimes 0 means pass, and vice-versa 确保知道通过/失败的返回值指示,因为有时0表示通过,反之亦然
  3. Check to see if GetLastError is noted, and decide what value that info can give your app 检查是否记录了GetLastError ,并确定该信息可为您的应用提供的值

If robustness is a serious enough goal, you may consider it a worthy time-investment to see if you can do a somewhat fault-tolerant design with redundant means to get whatever it is you need. 如果稳健性是一个足够严重的目标,您可能会认为这是一个值得花时间投入,看看您是否可以使用冗余方法进行容错设计,以获得您需要的任何内容。 Many times with WinAPIs there's more than one way to get to the specific info or functionality you're looking for, and sometimes that means using other Windows libraries/frameworks that work in-conjunction with the WinAPIs. 很多时候使用WinAPI,有多种方法可以获得您正在寻找的特定信息或功能,有时这意味着使用与WinAPI结合使用的其他Windows库/框架。

For example, getting screen data can be done with straight WinAPIs, but a popular alternative is to use GDI+, which plays well with WinAPIs. 例如,获取屏幕数据可以使用直接WinAPI来完成,但一种流行的替代方法是使用GDI +,它可以很好地与WinAPI配合使用。

Yes. 是。 Suppose you don't check what a function returned and the program just continues after the function failure. 假设您没有检查函数返回的内容,程序在函数失败后继续运行。 What happens next? 接下来发生什么? How will you know why your program misbehaves long time later? 你怎么知道你的课程为什么长时间行为不端?

One quite reliable solution is to throw an exception, but this will require your code to be exception-safe. 一个非常可靠的解决方案是抛出异常,但这需要您的代码是异常安全的。

Yes. 是。 If a function can fail, then you should protect against it. 如果一个函数可能失败,那么你应该防止它。

One helpful way to categorise potential problems in code is by the potential causes of failure: 对代码中潜在问题进行分类的一种有用方法是潜在的失败原因:

  1. invalid operations in your code 代码中的无效操作
  2. invalid operations in client code (code that call yours, written by someone else) 客户端代码中的无效操作(调用您的代码,由其他人编写)
  3. external dependencies (file system, network connection etc.) 外部依赖项(文件系统,网络连接等)

In situation 1, it is enough to detect the error and not perform recovery, as this is a bug that should be fixable by you. 在情况1中,它足以检测错误而不执行恢复,因为这是一个应该由您修复的错误。

In situation 2, the error should be notified to client code (eg by throwing an exception). 在情况2中,应该将错误通知给客户端代码(例如,通过抛出异常)。

In situation 3, your code should recover as far as possible automatically, and notify any client code if necessary. 在情况3中,您的代码应尽可能自动恢复,并在必要时通知任何客户端代码。

In both situations 2 & 3, you should endeavour to make sure that your code recovers to a valid state, eg you should try to offer "strong exception guarentee" etc. 在情况2和3中,您应该努力确保您的代码恢复到有效状态,例如,您应该尝试提供“强烈的例外保护”等。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM