2011-04-04 19 views
7

最近我正在更新一些代碼,用於使用GetWindowDC - > CreateCompatibleDC - > CreateCompatibleBitmap - > SelectObject - > BitBlt - > GetDIBits系列WinAPI函數進行屏幕截圖。現在我檢查所有失敗的人,因爲他們可以並且有時會失敗。但是之後我必須通過刪除創建的位圖,刪除創建的dc以及釋放窗口dc來執行清理。在任何我見過的例子中 - 即使在MSDN上 - 相關函數(DeleteObject,DeleteDC < ReleaseDC)都不會檢查失敗,大概是因爲如果它們被檢索/創建OK,它們將始終被刪除/釋放OK。但是,他們仍然可以失敗。程序應該檢查WinAPI函數上「不應該」,但可以失敗的失敗嗎?

這只是一個值得注意的例子,因爲這些調用都緊挨着彼此。但偶爾還有其他的功能可以失敗,但實際上從來沒有做過。如GetCursorPos。或者只有在傳遞無效數據(例如FileTimeToSytemTime)時纔會失敗的函數。

那麼,檢查所有可能因故障失敗的功能是不是很好?或者有些確定不檢查?作爲一個推論,當檢查這些應該永不失敗的功能失敗時,什麼是合適的?拋出一個運行時異常,使用斷言,別的東西?

+1

MSDN示例通常似乎試圖讓事情看起來很容易,而不是顯示可靠的錯誤處理:我不會將它們用作專業化的基準。 – 2011-04-04 06:47:14

回答

4

是的。你永遠不知道什麼時候承諾的服務會因不工作而驚喜。即使出現意外,最好也報告錯誤。否則,你會發現你的客戶說你的應用程序不起作用,而且其原因將是一個完全的謎團;您將無法及時,有效地回覆您的客戶,並且雙方都輸了。

如果您將代碼組織爲始終執行此類檢查,那麼將下一個檢查添加到您調用的下一個API並不困難。

+0

我同意這並不困難,我確實有這些檢查,但我有一個嘮叨的感覺,因爲我知道,例如,DeleteDC函數先決條件(通過一個有效的,創建的DC)將確保成功,我寫代碼永遠不會被調用。就像做錯誤檢查一樣,以確保sin函數返回-1和+1之間的值。 – rotanimod 2011-04-04 06:24:43

+0

如果您信任您的函數始終返回-1和+1之間的值,則不必檢查。你相信微軟API(面向5000萬行)總是返回一個無錯誤的結果嗎? – 2011-04-04 06:28:22

+0

對於ReleaseDC?說實話,是的,我確實。好吧,我無論如何。我確實看到你的觀點並同意它。也許我應該減少庫存,因爲MSDN沒有對這些函數使用失敗檢查,考慮是誰寫的...... – rotanimod 2011-04-04 06:35:55

1

是的。假設你不檢查函數返回的是什麼,程序在函數失敗之後才繼續。接下來發生什麼?你怎麼知道爲什麼你的程序以後很長時間會出現故障?

一個相當可靠的解決方案是拋出一個異常,但這需要你的代碼是異常安全的。

+0

實現異常拋出是我想寫這個問題的原因。如果DeleteObject失敗,在拋出異常之前,我仍然應該將調用嵌套到DeleteDC和ReleaseDC。如果嵌套的DeleteDC失敗,在拋出異常之前,我仍然應該調用ReleaseDC。我開始認爲這是很多永遠不會運行的代碼。 – rotanimod 2011-04-04 06:28:27

+2

@rotanimod:通過使用RAII解決這個問題 - 只是將創建/獲取操作包裝到一個類中,該類將在其析構函數中執行相應的釋放操作。這將爲您節省很多清理代碼。 – sharptooth 2011-04-04 06:35:09

5

是否測試的問題取決於如果失敗會做什麼。大多數樣本在清理完成後退出,因此驗證正確的清理沒有任何用處,程序正在退出。

不檢查像GetCursorPos這樣的東西可能會導致錯誤,但取決於避免這種情況所需的代碼決定您是否應該檢查。如果檢查它會在您的所有電話周圍添加3條線路,那麼您最好承擔風險。但是,如果你有一個宏設置來處理它,那麼爲了以防萬一,添加該宏並不會造成什麼影響。

FileTimeToSystemTime被檢查取決於你傳遞給它。系統中的文件時間?可能安全地忽略它。從用戶輸入構建的自定義字符串?可能更好,以確保。

+0

來自系統的文件時間?等到有人在你的程序中輸入「LPT:」作爲文件名。 – 2011-04-04 06:40:21

+0

@Ira Baxter:您正在使用轉換實用程序來驗證輸入,而不是檢查最初爲您提供輸入的函數的返回值? – Guvante 2011-04-04 06:43:48

+0

我同意這一點;這取決於你在做什麼。例如,如果fclose()失敗,你會怎麼做?像這樣的某些函數不能真正返回任何有意義的錯誤代碼,所以它們的返回值往往被忽略。 – Luke 2011-04-04 16:30:48

1

是的。如果一個功能可以失敗,那麼你應該應對

一到分類代碼中可能出現的問題有幫助的方式是失敗的可能原因:

  1. 無效操作在你的代碼在客戶端代碼
  2. 非法操作(代碼調用你的,由 有人寫別的)
  3. 外部依賴(文件系統,網絡連接等)

在情況1中,它是足夠的,以檢測埃羅r並且不執行恢復,因爲這是一個應該由您解決的錯誤。

在情況2中,應將錯誤通知客戶端代碼(例如,通過拋出異常)。

在情況3中,您的代碼應儘可能自動恢復,並在必要時通知任何客戶端代碼。

在這兩種情況下,您都應該努力確保您的代碼恢復到有效狀態,例如,你應該嘗試提供"strong exception guarentee"

1

是的,你需要檢查,但如果你使用的是C++,你可以利用RAII並將清理留給你正在使用的各種資源。

另一種方法是混雜if-else語句,這真是醜陋而且容易出錯。

+0

我很熟悉這個if-else jumble你說的。但在這種情況下,RAII對我來說似乎不自然。所以在我的第一個例子中,圍繞每個WinAPI項目編寫一個包裝類?一個處理獲取/釋放DC的BorrowedDC類,一個CreatedDC類,一個CreatedObject類? – rotanimod 2011-04-04 06:33:20

+1

不幸的是,你需要創建大量的包裝。但對我來說,這是值得的。在完成它之後,我可以閱讀邏輯,而不必糾正錯誤處理內容的控制流程。 – 2011-04-04 06:42:08

+0

起初我很猶豫,但考慮到這個屏幕捕捉是我的程序中最關鍵的部分之一,它絕對值得一些額外的類來提高魯棒性。謝謝。 – rotanimod 2011-04-04 06:47:57

2

有趣的是,你提到GetCursorPos,因爲當傳遞的地址大於2Gb時,它在Wow64進程上失敗。它每次都失敗。該錯誤在Windows 7中得到了修復。

所以,是的,我認爲明智的做法是檢查錯誤,即使您不期望它們。

+0

哈!那麼你讓我在那裏。 GetCursorPos剛剛跳入我的腦海;我相信對於不可能失敗的功能有更好的選擇。然而,正如你所說,在'總是檢查'列中顯然是一個堅實的原因... – rotanimod 2011-04-04 06:41:28

+0

還有其他情況下,該功能也可能失敗。我以前也認爲它不會失敗並被抓住。即使是微軟的HTML幫助查看器也會遇到我提到的錯誤! – 2011-04-04 06:56:33

相關問題