Not
Bu sayfaya erişim yetkilendirme gerektiriyor. Oturum açmayı veya dizinleri değiştirmeyi deneyebilirsiniz.
Bu sayfaya erişim yetkilendirme gerektiriyor. Dizinleri değiştirmeyi deneyebilirsiniz.
COM, bir yöntemin veya işlev çağrısının başarısını veya başarısızlığını göstermek için HRESULT değerlerini kullanır. Çeşitli SDK üst bilgileri çeşitli HRESULT sabitleri tanımlar. WinError.h'de yaygın bir sistem genelinde kod kümesi tanımlanır. Aşağıdaki tabloda bu sistem genelindeki dönüş kodlarından bazıları gösterilmektedir.
| Sabit | Sayısal değer | Açıklama |
|---|---|---|
| E_ACCESSDENIED | 0x80070005 | Erişim reddedildi. |
| E_FAIL | 0x80004005 | Belirtilmemiş hata. |
| E_INVALIDARG | 0x80070057 | Geçersiz parametre değeri. |
| E_OUTOFMEMORY | 0x8007000E | Bellek yetersiz. |
| E_POINTER | 0x80004003 | NULL bir işaretçi değeri için yanlış şekilde geçirildi. |
| E_UNEXPECTED | 0x8000FFFF | Beklenmeyen durum. |
| S_OK | 0x0 | Başarı. |
| S_FALSE | 0x1 | Başarı. |
"E_" ön ekini içeren tüm sabitler hata kodlarıdır. hem S_OK hem de S_FALSE sabitleri başarı kodlarıdır. Büyük olasılıkla 99% COM yöntemi başarılı olduğunda S_OK döndürür; ama bu gerçeğin sizi yanıltmasına izin vermeyin. Bir yöntem başka başarı kodları döndürebilir, bu nedenle her zaman SUCCEEDED veya FAILED makrosunu kullanarak hataları test edin. Aşağıdaki örnek kod, bir işlev çağrısının başarısını test etmek için yanlış yolu ve doğru yolu gösterir.
// 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");
}
başarı kodu S_FALSE bahsetmeyi hak ediyor. Bazı yöntemler, kabaca bir başarısızlık olmayan negatif bir durumu ifade etmek için S_FALSE kullanır. Ayrıca "no-op"— yönteminin başarılı olduğunu ancak hiçbir etkisi olmadığını gösterebilir. Örneğin, CoInitializeEx işlevi, aynı iş parçacığından ikinci kez çağırırsanız S_FALSE döndürür. Kodunuzda S_OK ile S_FALSE arasında ayrım yapmanız gerekiyorsa, değeri doğrudan test etmelisiniz; ancak yine de kalan durumları aşağıdaki örnek kodda gösterildiği gibi işlemek için FAILED veya SUCCEEDED kullanmalısınız.
if (hr == S_FALSE)
{
// Handle special case.
}
else if (SUCCEEDED(hr))
{
// Handle general success case.
}
else
{
// Handle errors.
printf("Error!\n");
}
Bazı HRESULT değerleri, Windows'un belirli bir özelliğine veya alt sistemine özeldir. Örneğin, Direct2D grafik API'D2DERR_UNSUPPORTED_PIXEL_FORMAThata kodunu tanımlar. Bu, programın desteklenmeyen bir piksel biçimi kullandığı anlamına gelir. Windows belgeleri genellikle bir yöntemin döndürebileceği belirli hata kodlarının listesini verir. Ancak, bu listelerin kesin olduğunu düşünmemelisiniz. Bir yöntem her zaman belgelerde listelenmeyen bir HRESULT değeri döndürebilir. Yine, BAŞARILI ve BAŞARISIZ makrolarını kullanın. Belirli bir hata kodunu test ederseniz, varsayılan bir durum da ekleyin.
if (hr == D2DERR_UNSUPPORTED_PIXEL_FORMAT)
{
// Handle the specific case of an unsupported pixel format.
}
else if (FAILED(hr))
{
// Handle other errors.
}
Hata İşleme Desenleri
Bu bölümde, COM hatalarını yapılandırılmış bir şekilde işlemeye yönelik bazı desenler ele alınır. Her desenin avantajları ve dezavantajları vardır. Bir ölçüde, seçim bir zevk meselesidir. Mevcut bir proje üzerinde çalışıyorsanız, belirli bir stilin temelini oluşturan kodlama yönergelerine zaten sahip olabilir. Hangi deseni benimsediğinize bakılmaksızın, sağlam kod aşağıdaki kurallara uyar.
- HRESULTdöndüren her yöntem veya işlev için devam etmeden önce dönüş değerini denetleyin.
- Kaynakları kullanıldıktan sonra serbest bırakın.
- NULL işaretçileri gibi geçersiz veya başlatılmamış kaynaklara erişmeye çalışma.
- Bir kaynağı yayınladıktan sonra kullanmayı denemeyin.
Bu kuralları göz önünde bulundurarak, hataları işlemeye yönelik dört desen aşağıda belirtilmiştir.
İç içe koşul ifadeleri
Her çağrıdan sonra bir HRESULTdöndürdüğünde, başarıyı test etmek için if ifadesini kullanın. Ardından, sonraki yöntem çağrısını if ifadesinin kapsamına yerleştirin. Gerektiği kadar derin iç içe yerleştirilebilecek daha fazla , deyimlerinin iç içe yerleştirilebilmesiyle mümkündür. Bu modüldeki önceki kod örneklerinin tümü bu deseni kullansa da burada tekrar verilmiştir:
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;
}
Avantaj -ları
- Değişkenler minimum kapsamla bildirilebilir. Örneğin, pItem kullanılana kadar bildirılmaz.
- Her koşul bildiriminde ifadesi için belirli değişmezler doğrudur: Önceki tüm çağrılar başarılı olmuştur ve edinilen tüm kaynaklar hala geçerlidir. Önceki örnekte, program en içteki if ifadesine ulaştığında, hem pItem hem de pFileOpen'in geçerli olduğu bilinir.
- Arabirim işaretçilerinin ve diğer kaynakların ne zaman yayınlandığı açıktır. deyimi kaynağı alan çağrının hemen ardından geliyorsa, kaynağı sonunda serbest bırakırsınız.
Dezavantaj -ları
- Bazı insanlar derin iç içe geçmiş yapıları okumakta zorlanırlar.
- Hata işleme, diğer dallanma ve döngü deyimleriyle karıştırılır. Bu, genel program mantığını izlemeyi zorlaştırabilir.
Basamaklı if ifadeleri
Her yöntem çağrısının ardından başarılı olup olmadığını test etmek için bir if deyimi kullanın. Yöntem başarılı olursa, sonraki yöntem çağrısını bloğu içindeki'e yerleştirin. Ancak, ifadeleri için daha fazla yuvalamak yerine, her bir sonraki BAŞARILI testini önceki if bloğundan sonra yerleştirin. Herhangi bir yöntem başarısız olursa, önceki tüm BAŞARILI testler işlevin sonuna ulaşana kadar basit bir şekilde başarısız olur.
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;
}
Bu düzende, işlevin en sonunda kaynakları serbest bırakırsınız. Hata oluşursa, işlevden çıkıldığında bazı işaretçiler geçersiz olabilir. Geçersiz bir işaretçide Release çağrılması programın çökmesine (veya daha kötüsüne) neden olabilir, bu nedenle tüm işaretçileri NULL olarak başlatmanız ve bunları serbest bırakmadan önce NULL olup olmadıklarını kontrol etmeniz gerekir. Bu örnekte SafeRelease işlevi kullanılır; akıllı işaretçiler de iyi bir seçimdir.
Bu deseni kullanırsanız döngü yapılarına dikkat etmeniz gerekir. Bir döngünün içinde, herhangi bir çağrı başarısız olursa döngüden ayrılın.
Avantaj -ları
- Bu desen, "iç içe geçmiş ifler" deseninden daha az iç içe yerleştirme oluşturur.
- Genel denetim akışını görmek daha kolaydır.
- Kaynaklar kodun bir noktasında serbest bırakılır.
Dezavantaj -ları
- Tüm değişkenler, işlevin en üstünde bildirilmeli ve başlatılmalıdır.
- Bir çağrı başarısız olursa işlev, işlevden hemen çıkmak yerine birden çok gereksiz hata denetimi yapar.
- Denetim akışı bir hatadan sonra işlevde devam ettiğinden, işlevin gövdesi boyunca geçersiz kaynaklara erişmemeye dikkat etmeniz gerekir.
- Döngü içindeki hatalar özel bir durum gerektirir.
Başarısız'a Atla
Her yöntem çağrısından sonra başarısızlığı test edin (başarılı değil). Hata durumunda işlevin alt kısmındaki etikete atlayın. Etiketten sonra, ancak işlevden çıkmadan önce kaynakları serbest bırakın.
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;
}
Avantaj -ları
- Genel denetim akışını görmek kolaydır.
- FAILED denetiminden sonra kodun her noktasında etikete atlamadıysanız, önceki tüm çağrıların başarılı olduğu garanti edilir.
- Kaynaklar kodda tek bir yerde serbest bırakılır.
Dezavantaj -ları
- Tüm değişkenler, işlevin en üstünde bildirilmeli ve başlatılmalıdır.
- Bazı programcılar kodlarında goto kullanmayı sevmez. (Ancak, goto kullanımının yüksek oranda yapılandırılmış olduğu unutulmalı; kod hiçbir zaman geçerli işlev çağrısının dışına atlamaz.)
- goto ifadeleri başlatıcıları atlar.
Hata Oluştuğunda At
Bir etikete atlamak yerine, bir yöntem başarısız olduğunda bir özel durum oluşturabilirsiniz. İstisna güvenli kod yazmaya alışkınsanız, bu daha idiomatik bir C++ stili oluşturabilir.
#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.
}
}
Bu örnekte arabirim işaretçilerini yönetmek için CComPtr sınıfının kullanıldığına dikkat edin. Genellikle, kodunuz istisnalar oluşturuyorsa RAII (Kaynak Edinimi Başlatmadır) modelini takip etmeniz gerekir. Diğer bir ifadeyle, her kaynak, yıkıcısı kaynağın doğru şekilde serbest bırakıldığını garanti eden bir nesne tarafından yönetilmelidir. Bir istisna atılırsa, yıkıcının mutlaka çağrılacağı garanti edilir. Aksi takdirde, programınız kaynakları sızdırabilir.
Avantaj -ları
- Özel durum işleme kullanan mevcut kodla uyumludur.
- Standart Şablon Kitaplığı (STL) gibi özel durumlar oluşturan C++ kitaplıklarıyla uyumludur.
Dezavantaj -ları
- Bellek veya dosya tanıtıcıları gibi kaynakları yönetmek için C++ nesneleri gerektirir.
- Özel durum güvenli kod yazma konusunda iyi bir anlayış gerektirir.
Sonraki