Poznámka:
Přístup k této stránce vyžaduje autorizaci. Můžete se zkusit přihlásit nebo změnit adresáře.
Přístup k této stránce vyžaduje autorizaci. Můžete zkusit změnit adresáře.
Com používá hodnoty HRESULT označující úspěch nebo selhání volání metody nebo funkce. Různé hlavičky sady SDK definují různé konstanty HRESULT. V winError.h je definována společná sada kódů pro celý systém. Následující tabulka uvádí některé z těchto návratových kódů pro celý systém.
| Konstanta | Číselná hodnota | Popis |
|---|---|---|
| E_ACCESSDENIED | 0x80070005 | Přístup byl odepřen. |
| E_FAIL | 0x80004005 | Nezadaná chyba |
| E_INVALIDARG (neplatný argument) | 0x80070057 | Neplatná hodnota parametru |
| E_OUTOFMEMORY | 0x8007000E | Nedostatek paměti. |
| E_POINTER | 0x80004003 | NULL byl nesprávně předán jako hodnota ukazatele. |
| E_UNEXPECTED | 0x8000FFFF | Neočekávaný stav |
| S_OK | 0x0 | Úspěch. |
| S_FALSE | 0x1 | Úspěch. |
Všechny konstanty s předponou "E_" jsou kódy chyb. Konstanty S_OK a S_FALSE jsou kódy úspěchu. Pravděpodobně 99% metod COM vrací S_OK, když jsou úspěšné; ale nenechte se touto skutečností zmást. Metoda může vrátit další kódy úspěchu, takže vždy testujte chyby pomocí makra SUCCEEDED nebo FAILED. Následující příklad kódu ukazuje nesprávný způsob a správný způsob, jak otestovat úspěch volání funkce.
// Wrong.
HRESULT hr = SomeFunction();
if (hr != S_OK)
{
printf("Error!\n"); // Bad. hr might be another success code.
}
// Right.
HRESULT hr = SomeFunction();
if (FAILED(hr))
{
printf("Error!\n");
}
Kód úspěchu S_FALSE zaslouží zmínku. Některé metody používají S_FALSE, aby přibližně znamenaly negativní podmínku, která není selháním. Může také naznačovat "no-op"– metoda byla úspěšná, ale neměla žádný vliv. Například funkce CoInitializeEx vrátí S_FALSE, pokud ji zavoláte podruhé ze stejného vlákna. Pokud potřebujete rozlišovat mezi S_OK a S_FALSE v kódu, měli byste hodnotu otestovat přímo, ale přesto použít NEÚSPĚŠNÉ nebo SUCCEEDED zpracovat zbývající případy, jak je znázorněno v následujícím ukázkovém kódu.
if (hr == S_FALSE)
{
// Handle special case.
}
else if (SUCCEEDED(hr))
{
// Handle general success case.
}
else
{
// Handle errors.
printf("Error!\n");
}
Některé hodnoty HRESULT jsou specifické pro konkrétní funkci nebo subsystém Windows. Například rozhraní API pro grafiku Direct2D definuje kód chyby D2DERR_UNSUPPORTED_PIXEL_FORMAT, což znamená, že program použil nepodporovaný formát pixelů. Dokumentace k Systému Windows často obsahuje seznam konkrétních kódů chyb, které může metoda vrátit. Tyto seznamy byste ale neměli považovat za konečné. Metoda může vždy vrátit hodnotu HRESULT, která není uvedená v dokumentaci. Znovu použijte makra SUCCEEDED a FAILED. Pokud testujete konkrétní kód chyby, uveďte také výchozí případ.
if (hr == D2DERR_UNSUPPORTED_PIXEL_FORMAT)
{
// Handle the specific case of an unsupported pixel format.
}
else if (FAILED(hr))
{
// Handle other errors.
}
Vzory pro zpracování chyb
Tato část se zabývá některými vzory pro zpracování chyb modelu COM strukturovaným způsobem. Každý vzor má výhody a nevýhody. Do určité míry je volba otázkou chuti. Pokud pracujete na existujícím projektu, může už mít pokyny pro kódování, které připisují určitý styl. Bez ohledu na to, jaký vzor použijete, bude robustní kód dodržovat následující pravidla.
- Pro každou metodu nebo funkci, která vrací HRESULT, před pokračováním zkontrolujte návratovou hodnotu.
- Uvolněte prostředky po jejich použití.
- Nepokoušejte se o přístup k neplatným nebo neinicializovaným prostředkům, jako jsou například ukazatele s hodnotou NULL.
- Nepokoušejte se použít prostředek po jeho uvolnění.
S ohledem na tato pravidla jsou zde čtyři vzory pro zpracování chyb.
Vnořené ifs
Po každém volání, které vrátí HRESULT, použijte příkaz if k testování úspěšnosti. Potom vložte další volání metody do rozsahu pokynu if v rámci. Může být použito více , pokud lze příkazy vnořit podle potřeby. V předchozích příkladech kódu v tomto modulu se tento vzor použil, ale tady je to znovu:
HRESULT ShowDialog()
{
IFileOpenDialog *pFileOpen;
HRESULT hr = CoCreateInstance(__uuidof(FileOpenDialog), NULL,
CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&pFileOpen));
if (SUCCEEDED(hr))
{
hr = pFileOpen->Show(NULL);
if (SUCCEEDED(hr))
{
IShellItem *pItem;
hr = pFileOpen->GetResult(&pItem);
if (SUCCEEDED(hr))
{
// Use pItem (not shown).
pItem->Release();
}
}
pFileOpen->Release();
}
return hr;
}
Výhody
- Proměnné lze deklarovat s minimálním rozsahem. Například pItem není deklarován, dokud se nepoužije.
- V každém příkazu platí určité invarianty: Všechna předchozí volání byla úspěšná a všechny získané prostředky jsou stále platné. V předchozím příkladu, když program dosáhne nejvnitřnějšího příkazu if, oba pItem i pFileOpen jsou známy jako platné.
- Je jasné, kdy uvolnit ukazatele rozhraní a další prostředky. Prostředek uvolníte na konci části , pokud je podmínka v příkazu, který okamžitě následuje za voláním, které prostředek získal.
Nevýhody
- Někteří lidé shledávají hluboké vnoření těžkým na čtení.
- Zpracování chyb je propojené s dalšími příkazy pro větvení a smyčky. To může znesnadnit sledování celkové logiky programu.
Kaskádové ifs
Po každém volání metody použijte , jestliže, k otestování úspěchu. Pokud metoda uspěje, umístěte další volání metody do bloku if. Místo vnořování dalších příkazů „if“, umístěte každý následující SUCCEEDED test po předchozím bloku „if“. Pokud jakákoli metoda selže, všechny zbývající SUCCEEDED testy jednoduše selžou, dokud nedosáhnete dolní části funkce.
HRESULT ShowDialog()
{
IFileOpenDialog *pFileOpen = NULL;
IShellItem *pItem = NULL;
HRESULT hr = CoCreateInstance(__uuidof(FileOpenDialog), NULL,
CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&pFileOpen));
if (SUCCEEDED(hr))
{
hr = pFileOpen->Show(NULL);
}
if (SUCCEEDED(hr))
{
hr = pFileOpen->GetResult(&pItem);
}
if (SUCCEEDED(hr))
{
// Use pItem (not shown).
}
// Clean up.
SafeRelease(&pItem);
SafeRelease(&pFileOpen);
return hr;
}
V tomto modelu uvolníte prostředky na samém konci funkce. Pokud dojde k chybě, některé ukazatele můžou být při ukončení funkce neplatné. Volání Release na neplatném ukazateli způsobí selhání programu (nebo horší), takže je nutné inicializovat všechny ukazatele na NULL a před uvolněním zkontrolovat, jestli jsou NULL. Tento příklad používá funkci SafeRelease; inteligentní ukazatele jsou také dobrou volbou.
Pokud použijete tento vzor, musíte být opatrní s cyklickými konstrukcemi. Uvnitř smyčky přerušte smyčku, pokud jakékoli volání selže.
Výhody
- Tento vzorec vytváří méně vnoření než vzorec vnořených if.
- Celkový tok řízení je přehlednější.
- Prostředky se uvolňují na jednom místě v kódu.
Nevýhody
- Všechny proměnné musí být deklarovány a inicializovány v horní části funkce.
- Pokud volání selže, funkce provede několik nepotřebných kontrol chyb místo okamžitého ukončení funkce.
- Vzhledem k tomu, že řízení pokračuje funkcí i po selhání, musíte být opatrní po celou dobu trvání funkce, abyste nepřistupovali k neplatným prostředkům.
- Chyby uvnitř smyčky vyžadují zvláštní zacházení.
Přeskočit na selhání
Po každém volání metody otestujte selhání (ne úspěch). Při selhání přejděte na popisek v dolní části funkce. Za popiskem, ale před ukončením funkce, uvolněte prostředky.
HRESULT ShowDialog()
{
IFileOpenDialog *pFileOpen = NULL;
IShellItem *pItem = NULL;
HRESULT hr = CoCreateInstance(__uuidof(FileOpenDialog), NULL,
CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&pFileOpen));
if (FAILED(hr))
{
goto done;
}
hr = pFileOpen->Show(NULL);
if (FAILED(hr))
{
goto done;
}
hr = pFileOpen->GetResult(&pItem);
if (FAILED(hr))
{
goto done;
}
// Use pItem (not shown).
done:
// Clean up.
SafeRelease(&pItem);
SafeRelease(&pFileOpen);
return hr;
}
Výhody
- Celkový tok řízení je snadno vidět.
- Na každém místě v kódu po neúspěšné kontrole , pokud jste nepřeskočili na popisek, je zaručeno, že všechna předchozí volání byla úspěšná.
- Prostředky se uvolňují na jednom místě v kódu.
Nevýhody
- Všechny proměnné musí být deklarovány a inicializovány v horní části funkce.
- Někteří programátoři nerad používají goto v kódu. (Je však třeba poznamenat, že toto použití goto je vysoce strukturované; kód nikdy nepřeskočí mimo volání aktuální funkce.)
- goto příkazy přeskočí inicializátory.
Vyvolání selhání
Místo přeskočení k popisku můžete vyvolat výjimku, když metoda selže. Pokud jste zvyklí na psaní kódu bezpečného pro výjimky, může to vést k idiomatickému stylu jazyka C++.
#include <comdef.h> // Declares _com_error
inline void throw_if_fail(HRESULT hr)
{
if (FAILED(hr))
{
throw _com_error(hr);
}
}
void ShowDialog()
{
try
{
CComPtr<IFileOpenDialog> pFileOpen;
throw_if_fail(CoCreateInstance(__uuidof(FileOpenDialog), NULL,
CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&pFileOpen)));
throw_if_fail(pFileOpen->Show(NULL));
CComPtr<IShellItem> pItem;
throw_if_fail(pFileOpen->GetResult(&pItem));
// Use pItem (not shown).
}
catch (_com_error err)
{
// Handle error.
}
}
Všimněte si, že v tomto příkladu se ke správě ukazatelů rozhraní používá CComPtr třídy. Obecně platí, že pokud váš kód vyhazuje výjimky, měli byste se řídit vzorem RAII (Získání prostředků je inicializace). To znamená, že každý prostředek by měl být spravován objektem, jehož destruktor zaručuje správné uvolnění prostředku. Pokud je vyvolána výjimka, je zaručeno vyvolání destruktoru. V opačném případě může váš program způsobit únik prostředků.
Výhody
- Kompatibilní s existujícím kódem, který používá zpracování výjimek.
- Kompatibilní s knihovnami C++, které vyvolává výjimky, jako je standardní knihovna šablon (STL).
Nevýhody
- Vyžaduje použití objektů C++ pro správu prostředků, jako je paměť nebo popisovače souborů.
- Vyžaduje dobré znalosti o tom, jak napsat kód bezpečný pro výjimky.
Další