Megosztás a következőn keresztül:


COM kódolási eljárások

Ez a témakör ismerteti, hogyan teheti hatékonyabbá és robusztusabbá a COM-kódját.

A __uuidof operátor

A program létrehozásakor a következőhöz hasonló linkerhibák jelenhetnek meg:

unresolved external symbol "struct _GUID const IID_IDrawable"

Ez a hiba azt jelenti, hogy a GUID-állandó külső kapcsolattal (extern) lett deklarálva, és a hivatkozásszolgáltató nem találta az állandó definícióját. A GUID-állandó értékét általában statikus kódtárfájlból exportálják. Ha Microsoft Visual C++-ot használ, akkor a __uuidof operátorral elkerülheti, hogy statikus kódtárat csatoljon. Ez az operátor a Microsoft nyelvi bővítménye. Egy kifejezés GUID-értékét adja vissza. A kifejezés lehet felülettípusnév, osztálynév vagy illesztőmutató. A __uuidofhasználatával az alábbiak szerint hozhatja létre a Common Item Dialog objektumot:

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

A fordító kinyeri a GUID-értéket a fejlécből, ezért nincs szükség tárexportálásra.

Jegyzet

A GUID-érték a típusnévhez a fejlécben __declspec(uuid( ... )) deklarálásával van társítva. További információ: __declspec dokumentációja a Visual C++ dokumentációjában.

 

A IID_PPV_ARGS makró

Láttuk, hogy CoCreateInstance és QueryInterface megköveteli a végső paraméter kényszerítését egy void** típusra. Ez egy típuseltérés lehetőségét teremti meg. Vegye figyelembe a következő kódrészletet:

// 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**.
    );

Ez a kód az IFileDialogCustomize felületet kéri, de egy IFileOpenDialog mutatót ad át. A reinterpret_cast kifejezés megkerüli a C++ típusrendszert, így a fordító nem fogja elkapni ezt a hibát. A legjobb esetben, ha az objektum nem implementálja a kért felületet, a hívás egyszerűen meghiúsul. A legrosszabb esetben a függvény sikeres, és egy nem egyező mutatóval rendelkezik. Más szóval a mutató típusa nem egyezik a memóriában lévő tényleges virtuális táblával. Ahogy el tudod képzelni, ezen a ponton semmi jó nem történhet.

Jegyzet

A virtuális (virtuálismetódus-táblázat) függvénymutatók táblázata. A virtuális tábla az, ahogyan a COM egy metódushívást futtatáskor köt a megvalósításhoz. Nem véletlen, hogy a virtuálistáblák a legtöbb C++ fordító virtuális metódusokat implementálnak.

 

A IID_PPV_ARGS makró segít elkerülni ezt a hibaosztályt. A makró használatához cserélje le a következő kódot:

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

a következővel:

IID_PPV_ARGS(&pFileOpen)

A makró automatikusan beszúrja a __uuidof(IFileOpenDialog) a felületazonosítóhoz, így garantáltan megfelel a mutató típusának. Itt található a módosított (és helyes) kód:

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

Ugyanezt a makrót használhatja QueryInterface:

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

A SafeRelease minta

A referenciák számlálása a programozás egyik ilyen része, amely alapvetően egyszerű, de fárasztó is, ami megkönnyíti a hiba megoldását. A tipikus hibák a következők:

  • Ha végzett a használattal, nem sikerült feloldani az illesztőmutatót. Ez a hibaosztály memóriavesztést és más erőforrásokat okoz a program számára, mert az objektumok nem pusztulnak el.
  • A Release meghívása érvénytelen mutatóval. Ez a hiba például akkor fordulhat elő, ha az objektum soha nem lett létrehozva. Ez a hibakategória valószínűleg a program összeomlását okozza.
  • A Kiadási meghívása után elhalasztja a felületmutatót. Ez a hiba a program összeomlását okozhatja. Rosszabb esetben előfordulhat, hogy a program később véletlenszerűen összeomlik, ami megnehezíti az eredeti hiba nyomon követését.

A hibák elkerülésének egyik módja, ha meghívja Release egy függvényen keresztül, amely biztonságosan felszabadítja a mutatót. Az alábbi kód egy függvényt mutat be, amely ezt végzi:

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

Ez a függvény egy COM-felületi mutatót vesz fel paraméterként, és az alábbiakat hajtja végre:

  1. Ellenőrzi, hogy a mutató null -e.
  2. Meghívja kiadási, ha a mutató nem NULL.
  3. Az egérmutatót null értékre állítja.

Íme egy példa a SafeReleasehasználatára:

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);
}

Ha CoCreateInstance sikeres, a SafeRelease hívása felengedi a mutatót. Ha CoCreateInstance sikertelen, a pFileOpenNULLmarad. A SafeRelease függvény ezt ellenőrzi, és kihagyja a Kiadásihívását.

A SafeRelease is nyugodtan hívhatja meg többször ugyanazon a mutatón, ahogy az itt látható:

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

COM Smart Pointers

A SafeRelease függvény hasznos, de két dolgot kell megjegyeznie:

  • Inicializálja az összes illesztőmutatót NULL.
  • Hívja fel SafeRelease, mielőtt minden mutató kimegy a hatókörből.

C++ programozóként valószínűleg arra gondol, hogy nem kell emlékeznie ezekre a dolgokra. Végül is ezért vannak a C++ konstruktorai és destruktorai. Jó lenne, ha lenne egy osztály, amely körbefuttatja a mögöttes felület mutatót, és automatikusan inicializálja és felszabadítja a mutatót. Más szóval a következőhöz hasonlót szeretnénk:

// Warning: This example is not complete.

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

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

Az itt látható osztálydefiníció hiányos, és nem használható az ábrán látható módon. Legalább meg kell határoznia egy példánykonstruktort, egy hozzárendelési operátort, és meg kell határoznia a mögöttes COM-mutató elérésének módját. Szerencsére nem kell elvégeznie ezt a munkát, mert a Microsoft Visual Studio már biztosít egy intelligens mutatóosztályt az Active Template Library (ATL) részeként.

Az ATL intelligens mutatóosztály neve CComPtr. (Van egy CComQIPtr osztály is, amelyet itt nem tárgyalunk.) Íme a Párbeszédpanel megnyitása példa, amelyet CComPtrhasznál.

#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;
}

A kód és az eredeti példa közötti fő különbség az, hogy ez a verzió nem hívja meg explicit módon kiadási. Amikor a CComPtr-példány hatókörén kívül esik, a destruktor meghívja kiadási az alapul szolgáló mutatón.

CComPtr osztálysablon. A sablon argumentuma a COM-felület típusa. Belsőleg CComPtr ilyen típusú mutatót tartalmaz. CComPtr felülbírálja operátor->() és operátor&(), hogy az osztály az alapul szolgáló mutatóhoz hasonlóan viselkedik. A következő kód például egyenértékű az IFileOpenDialog meghívásával:: metódus megjelenítése közvetlenül:

hr = pFileOpen->Show(NULL);

CComPtr egy CComPtr::CoCreateInstance metódust is definiál, amely néhány alapértelmezett paraméterértékkel meghívja a COM CoCreateInstance függvényt. Az egyetlen kötelező paraméter az osztályazonosító, ahogy a következő példa is mutatja:

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

A CComPtr::CoCreateInstance metódus kizárólag kényelmi célokat szolgál; ha szeretné, továbbra is meghívhatja a COM CoCreateInstance függvényt.

Következő

hibakezelés a COM