簡體   English   中英

使用GetLastError()的正確方法(時間)是什么?

[英]What is the REALLY correct way (time) to use GetLastError()?

在Stack Overflow(現在已被Powers-that-Be清理)的擴展參數之后,出現了關於何時應該真正調用GetLastError函數的問題。

注意:這不是關於樣式的問題,只是關於:(a)在“非純粹”代碼的情況下標准保證(或不保證); (b)在安全的winapi編程方面,什么是“最佳實踐”。

這是一個例子(根據發布的原始問題改編):

#include <windows.h>
#include <stdio.h>
#include <iostream>

//#define PURIST 1

using namespace std;
int main()
{
    HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE);
    SMALL_RECT wSize = { 0,0,60,20 }; // Works on my screen!
    if (hConsole == nullptr) {
        cout << "Console Handle is Null" << endl;
        return 1;
    }
    else {
        char message[256];
        if (!SetConsoleWindowInfo(hConsole, TRUE, &wSize)) {
            #ifdef PURIST
            // 'Purist' code ...
            DWORD eCode = GetLastError();
            sprintf(message, "SetConsoleWindowInfo failed; code = %d!", eCode);
            #else
            // More normal code ...
            sprintf(message, "SetConsoleWindowInfo failed; code = %d!", GetLastError());
            #endif
        }
        else {
            strcpy(message, "SetConsoleWindowInfo call succeeded!");
        }
        cout << message << endl;
    }
    getchar(); // Just to stop console closing!
    return 0;
}

顯然,“純粹主義”的方法能正常運作! 但是, c++語言標准是否保證“正常”方法也有效? (也就是說,可以確定作為sprintf的參數的GetLastError()將是在測試SetConsoleWindowInfo()的返回值后執行的第一個代碼嗎?)

PS:請不要過分嚴厲地評價我的代碼質量! 正如我所說,這是對原始問題的改編。

編輯:一個更典型的情況(我在我的Windows應用程序中使用了很多)看起來像這樣:

if (<WinApi call failed>) {
    TCHAR eText[256];
    FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, nullptr, GetLastError(), 0, eText, 255, nullptr);
    // Do something with eText, etc. ...
    return <error code>;
}

但同樣, GetLastError()是唯一的非const參數。

我將這個問題分成兩個獨立的問題:

  1. “普通代碼”示例是否按預期工作?
    是的,這個確切的代碼將按預期工作。 其他函數參數是內置類型,即使沒有定義訂單函數參數的評估,它們都不會干擾GetLastError調用。
  2. “普通代碼”示例是調用GetLastError的正確方法嗎?
    它不是,即使運行此代碼會產生預期的結果。 原因是使用GetLastError作為函數參數會對其他函數參數引入隱式約束 ,它們必須沒有最后錯誤更改副作用。 這使得代碼更容易出錯並且更難以維護。

因此經驗法則是在調用其他函數或創建/銷毀對象之前將最后一個錯誤值存儲在某個變量中。

非純粹的方式在這里很好

在調用函數時(無論函數是否為內聯函數,以及是否使用了顯式函數調用語法),與任何參數表達式或指定被調用函數的后綴表達式相關聯的每個值計算和副作用都會被排序執行被調用函數體中的每個表達式或語句。

規則3 ,我認為這對應於C ++ 17標准草案版本中的§8.2.2.5。

在這種情況下,沒有任何其他副作用的論點。

但請注意,它很脆弱,如果有人做頑皮的事情

#define sprintf (log_call_to_sprintf(), sprintf)

你運氣不好

讓我們有下一個代碼片段

if (<WinApi call failed>) {
    SomeApiCall(arg<1>, .., GetLastError(), .., arg<n>);
}

並假設下一個條件:

  1. SomeApiCall不是宏擴展到其他東西 - 這正是一些函數調用
  2. 函數的所有參數( arg<1> ... arg<n> ),除GetLastError() ,僅使用內部c / c ++語言表達式 ,無需任何外部函數調用(包括語言支持庫)
  3. 參數中沒有任何內容,什么可以導致硬件異常,如除以零,指針引用內存等

  4. 這不是關於編碼風格的問題

例如這個電話

SomeApiCall( "some text", GetLastError(), 8);

符合2和3

在這種情況下,我們可以說,在<WinApi call failed>之后,可以在更改線程的最后錯誤代碼值之前調用GetLastError() 因為:

  • 線程的最后錯誤代碼只能由於某些windows api調用而更改
  • 函數的所有參數( SomeApiCall執行被調用函數體內的每個表達式或語句之前計算
  • 所以在<WinApi call failed >和SomeApiCall只存在arg<1> ,.., GetLastError() ,.., arg<n>
  • 到期2和3 - 沒有任何直接或間接(在異常處理程序中)windows api調用,因為c / c ++ 語言不了解windows api不能通過自己調用它(沒有一些外部語言調用,包括語言支持庫)
  • 所以在<WinApi call failed >和GetLastError()調用 - 沒有任何windows api調用
  • 因此,在<WinApi call failed >和GetLastError()調用之間不會更改最后錯誤代碼的結果值

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM