Aracılığıyla paylaş


COM Kodlama Uygulamaları

Bu konu başlığında, COM kodunuzu daha etkili ve sağlam hale getirmenin yolları açıklanmaktadır.

  • __uuidof İşleci
  • IID_PPV_ARGS Makro
  • SafeRelease Deseni
  • COM Akıllı İşaretçileri

__uuidof İşleci

Programınızı oluştururken aşağıdakine benzer bağlayıcı hataları alabilirsiniz:

unresolved external symbol "struct _GUID const IID_IDrawable"

Bu hata, bir GUID sabitinin dış bağlantı (extern) ile bildirildiği ve bağlayıcının sabitin tanımını bulamadığı anlamına gelir. GUID sabitinin değeri genellikle statik kitaplık dosyasından dışarı aktarılır. Microsoft Visual C++ kullanıyorsanız, __uuidof işlecini kullanarak statik bir kitaplığı bağlama gereğini önleyebilirsiniz. Bu işleç bir Microsoft dil uzantısıdır. bir ifadeden GUID değeri döndürür. İfade bir arabirim türü adı, sınıf adı veya arabirim işaretçisi olabilir. __uuidofkullanarak Ortak Öğe İletişim Kutusu nesnesini aşağıdaki gibi oluşturabilirsiniz:

IFileOpenDialog *pFileOpen;
hr = CoCreateInstance(__uuidof(FileOpenDialog), NULL, CLSCTX_ALL, 
    __uuidof(pFileOpen), reinterpret_cast<void**>(&pFileOpen));

Derleyici üst bilgiden GUID değerini ayıklar, bu nedenle kitaplık dışarı aktarma işlemi gerekmez.

Not

GUID değeri, üst bilgide __declspec(uuid( ... )) bildirilerek tür adıyla ilişkilendirilir. Daha fazla bilgi için Visual C++ belgelerindeki __declspec belgelerine bakın.

 

IID_PPV_ARGS Makro

Hem CoCreateInstance hem de QueryInterface son parametreyi void** türüne zorlama gerektirdiğini gördük. Bu, tür uyuşmazlığı potansiyeli oluşturur. Aşağıdaki kod parçasını göz önünde bulundurun:

// Wrong!

IFileOpenDialog *pFileOpen;

hr = CoCreateInstance(
    __uuidof(FileOpenDialog), 
    NULL, 
    CLSCTX_ALL, 
    __uuidof(IFileDialogCustomize),       // The IID does not match the pointer type!
    reinterpret_cast<void**>(&pFileOpen)  // Coerce to void**.
    );

Bu kod IFileDialogCustomize arabirimini ister, ancak IFileOpenDialog işaretçisini geçirir. reinterpret_cast ifadesi C++ tür sistemini atlatır, böylece derleyici bu hatayı yakalamaz. En iyi durumda, nesne istenen arabirimi uygulamazsa, çağrı başarısız olur. En kötü durumda, işlev başarılı olur ve eşleşmeyen bir işaretçiniz vardır. Başka bir deyişle, işaretçi türü bellekteki gerçek vtable ile eşleşmiyor. Tahmin edebileceğiniz gibi, o noktada iyi bir şey olamaz.

Not

vtable (sanal yöntem tablosu), işlev işaretçileri tablosudur. vtable, COM'un çalışma zamanında bir yöntem çağrısını uygulamasına nasıl bağladığını gösterir. Vtable'lar, çoğu C++ derleyicilerinin sanal yöntemleri uygulama şeklidir.

 

IID_PPV_ARGS makro, bu hata sınıfını önlemeye yardımcı olur. Bu makroyu kullanmak için aşağıdaki kodu değiştirin:

__uuidof(IFileDialogCustomize), reinterpret_cast<void**>(&pFileOpen)

şununla:

IID_PPV_ARGS(&pFileOpen)

Makro, arabirim tanımlayıcısı için otomatik olarak __uuidof(IFileOpenDialog) ekler, bu nedenle işaretçi türüyle eşleşmesi garanti edilir. Değiştirilen (ve doğru) kod şu şekildedir:

// Right.
IFileOpenDialog *pFileOpen;
hr = CoCreateInstance(__uuidof(FileOpenDialog), NULL, CLSCTX_ALL, 
    IID_PPV_ARGS(&pFileOpen));

QueryInterfaceile aynı makroyu kullanabilirsiniz:

IFileDialogCustomize *pCustom;
hr = pFileOpen->QueryInterface(IID_PPV_ARGS(&pCustom));

SafeRelease Deseni

Başvuru sayma, programlamada temelde kolay olan, ancak aynı zamanda yorucu olan şeylerden biridir ve bu da hata almayı kolaylaştırır. Tipik hatalar şunlardır:

  • Bir arabirim işaretçisini kullanmayı bitirdiğinizde serbest bırakama. Bu hata sınıfı, nesneler yok edilmediğinden programınızın belleği ve diğer kaynakları sızdırmasına neden olur.
  • Geçersiz bir işaretçiyle Release çağrılır. Örneğin, nesne hiç oluşturulmadıysa bu hata oluşabilir. Bu hata kategorisi büyük olasılıkla programınızın kilitlenmesine neden olur.
  • Release çağrıldıktan sonra arabirim işaretçisi başvurusu kaldırma. Bu hata programınızın kilitlenmesine neden olabilir. Daha da kötüsü, programınızın daha sonra rastgele kilitlenmesine neden olabilir ve bu da özgün hatayı izlemeyi zorlaştırabilir.

Bu hataları önlemenin bir yolu, işaretçiyi güvenli bir şekilde serbest bırakan bir işlev aracılığıyla Release çağırmaktır. Aşağıdaki kodda bunu sağlayan bir işlev gösterilmektedir:

template <class T> void SafeRelease(T **ppT)
{
    if (*ppT)
    {
        (*ppT)->Release();
        *ppT = NULL;
    }
}

Bu işlev bir COM arabirim işaretçisini parametre olarak alır ve aşağıdakileri yapar:

  1. İşaretçinin NULL olup olmadığını denetler.
  2. İşaretçi NULL değilse Release çağırır.
  3. İşaretçiyi NULL olarak ayarlar.

SafeReleasekullanma örneği aşağıda verilmiştir:

void UseSafeRelease()
{
    IFileOpenDialog *pFileOpen = NULL;

    HRESULT hr = CoCreateInstance(__uuidof(FileOpenDialog), NULL, 
        CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&pFileOpen));
    if (SUCCEEDED(hr))
    {
        // Use the object.
    }
    SafeRelease(&pFileOpen);
}

CoCreateInstance başarılı olursa, SafeRelease çağrısı işaretçiyi serbest bırakır. CoCreateInstance başarısız olursa, pFileOpen NULL kalır. SafeRelease işlevi bunu denetler ve Releaseçağrısına atlar.

Burada gösterildiği gibi, SafeRelease aynı işaretçide birden çok kez çağırmak da güvenlidir:

// Redundant, but OK.
SafeRelease(&pFileOpen);
SafeRelease(&pFileOpen);

COM Akıllı İşaretçileri

SafeRelease işlevi kullanışlıdır, ancak iki şeyi hatırlamanızı gerektirir:

  • NULL için tüm arabirim işaretçilerini başlatın.
  • Her işaretçi kapsamın dışına çıkmadan önce SafeRelease çağırın.

Bir C++ programcısı olarak, muhtemelen bunların ikisini de hatırlamamanız gerektiğini düşünüyorsunuzdur. Sonuçta, C++'ın oluşturucuları ve yıkıcıları olmasının nedeni budur. Temel arabirim işaretçisini sarmalayan ve işaretçiyi otomatik olarak başlatan ve serbest bırakan bir sınıfın olması iyi olur. Başka bir deyişle, aşağıdakine benzer bir şey istiyoruz:

// Warning: This example is not complete.

template <class T>
class SmartPointer
{
    T* ptr;

 public:
    SmartPointer(T *p) : ptr(p) { }
    ~SmartPointer()
    {
        if (ptr) { ptr->Release(); }
    }
};

Burada gösterilen sınıf tanımı eksik ve gösterildiği gibi kullanılamaz. En azından bir kopya oluşturucu, atama işleci ve temel com işaretçisine erişmek için bir yol tanımlamanız gerekir. Neyse ki, Microsoft Visual Studio zaten Etkin Şablon Kitaplığı'nın (ATL) bir parçası olarak bir akıllı işaretçi sınıfı sağladığından, bu işlerin hiçbirini yapmanız gerekmez.

ATL akıllı işaretçi sınıfı, CComPtrolarak adlandırılır. (Burada tartışılmayan bir CComQIPtr sınıfı da vardır.) CComPtrkullanmak için Aç İletişim Kutusu örneği aşağıda verilmiştir.

#include <windows.h>
#include <shobjidl.h> 
#include <atlbase.h> // Contains the declaration of CComPtr.

int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE, PWSTR pCmdLine, int nCmdShow)
{
    HRESULT hr = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED | 
        COINIT_DISABLE_OLE1DDE);
    if (SUCCEEDED(hr))
    {
        CComPtr<IFileOpenDialog> pFileOpen;

        // Create the FileOpenDialog object.
        hr = pFileOpen.CoCreateInstance(__uuidof(FileOpenDialog));
        if (SUCCEEDED(hr))
        {
            // Show the Open dialog box.
            hr = pFileOpen->Show(NULL);

            // Get the file name from the dialog box.
            if (SUCCEEDED(hr))
            {
                CComPtr<IShellItem> pItem;
                hr = pFileOpen->GetResult(&pItem);
                if (SUCCEEDED(hr))
                {
                    PWSTR pszFilePath;
                    hr = pItem->GetDisplayName(SIGDN_FILESYSPATH, &pszFilePath);

                    // Display the file name to the user.
                    if (SUCCEEDED(hr))
                    {
                        MessageBox(NULL, pszFilePath, L"File Path", MB_OK);
                        CoTaskMemFree(pszFilePath);
                    }
                }

                // pItem goes out of scope.
            }

            // pFileOpen goes out of scope.
        }
        CoUninitialize();
    }
    return 0;
}

Bu kodla özgün örnek arasındaki temel fark, bu sürümün Release'ı açıkça çağırmamasıdır. CComPtr örneği kapsam dışına çıktığında, yıkıcı temel işaretçide Release çağırır.

CComPtr bir sınıf şablonudur. Şablon bağımsız değişkeni COM arabirim türüdür. dahili olarak, CComPtr bu tür bir işaretçi tutar. CComPtrişlecini geçersiz kılar->() ve işleci&(), böylece sınıf temel alınan işaretçi gibi davranır. Örneğin, aşağıdaki kod IFileOpenDialog::Show yöntemini doğrudan çağırmaya eşdeğerdir:

hr = pFileOpen->Show(NULL);

CComPtr ayrıca com CoCreateInstance işlevini bazı varsayılan parametre değerleriyle çağıran bir CComPtr::CoCreateInstance yöntemi tanımlar. Sonraki örnekte gösterildiği gibi tek gerekli parametre sınıf tanımlayıcısıdır:

hr = pFileOpen.CoCreateInstance(__uuidof(FileOpenDialog));

CComPtr::CoCreateInstance yöntemi yalnızca kolaylık sağlamak için sağlanır; dilerseniz COM CoCreateInstance işlevini çağırabilirsiniz.

Önümüzdeki

COM 'da hata işlemeyi