Megjegyzés
Az oldalhoz való hozzáféréshez engedély szükséges. Megpróbálhat bejelentkezni vagy módosítani a címtárat.
Az oldalhoz való hozzáféréshez engedély szükséges. Megpróbálhatja módosítani a címtárat.
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:
- Ellenőrzi, hogy a mutató null -e.
- Meghívja kiadási, ha a mutató nem NULL.
- 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ő