COM : Interface "custom" et smart pointers
Le
Bruno FREDERIC
Bonjour à tous,
Je développe un objet COM et un client COM en Visual C++6 SP6. L'objet COM
est généré grâce au "ATL COM AppWizard" et expose une interface de type
"custom".
Dans le client, je souhaite utiliser les SmartPointers de VC pour obtenir un
pointeur sur cette interface. Ce qui pose problème.
Lorsque j'implémente l'interface COM "custom" au sein d'une DLL, cela marche
très bien. Mais lorsque j'implémente cette même interface au sein d'un EXE
ou d'un service, je récupère systématiquement un pointeur "pCust" égal à
NULL dans mon client COM (code du client COM ci-dessous).
En revanche, une interface de type "dual" ne semble pas poser de problème :
j'ai rajouté un deuxième objet implémentant une interface "dual" à mon
serveur COM et je récupère bien le pointeur "pDual" sur cette interface
"dual" dans mon client ; que cet objet COM soit implémenté au sein d'une
DLL, d'un EXE ou d'un service.
J'ai inclus ci-dessous les définitions IDL des deux interfaces que j'ai
utilisées pour tous mes tests (elles ne comportent aucune méthode).
Les résultats de ces tests me laissent perplexe : quelque chose du
fonctionnement de COM doit m'échapper. Pourquoi est-ce que mon client
récupère un pointeur NULL sur une interface "custom" implémentée au sein
d'un EXE ou d'un service ?
- Client COM (Win32 Console Application) -
#import "..\ServeurCOM\ServeurCOM.tlb"
using namespace SERVEURCOMLib;
int main(int argc, char* argv[])
{
CoInitialize(NULL);
try {
// Objet avec interface "Custom" :
IObjetCustomPtr pCust(__uuidof(ObjetCustom));
printf("pCust=0x%X", pCust);
// Objet avec interface "Dual" :
IObjetDualPtr pDual(__uuidof(ObjetDual));
printf("pDual=0x%X", pDual);
}
catch (_com_error e) {
printf("%s", e.ErrorMessage());
}
CoUninitialize();
return 0;
}
- Définitions IDL de IObjetCustom et IObjetDual -
[
object,
uuid(18F1C4CF-3693-430A-8170-9733F7528F52),
helpstring("IObjetCustom Interface"),
pointer_default(unique)
]
interface IObjetCustom : IUnknown
{
};
[
object,
uuid(564B7B43-EFBA-45D6-864F-4457540409DD),
dual,
helpstring("IObjetDual Interface"),
pointer_default(unique)
]
interface IObjetDual : IDispatch
{
};
Je développe un objet COM et un client COM en Visual C++6 SP6. L'objet COM
est généré grâce au "ATL COM AppWizard" et expose une interface de type
"custom".
Dans le client, je souhaite utiliser les SmartPointers de VC pour obtenir un
pointeur sur cette interface. Ce qui pose problème.
Lorsque j'implémente l'interface COM "custom" au sein d'une DLL, cela marche
très bien. Mais lorsque j'implémente cette même interface au sein d'un EXE
ou d'un service, je récupère systématiquement un pointeur "pCust" égal à
NULL dans mon client COM (code du client COM ci-dessous).
En revanche, une interface de type "dual" ne semble pas poser de problème :
j'ai rajouté un deuxième objet implémentant une interface "dual" à mon
serveur COM et je récupère bien le pointeur "pDual" sur cette interface
"dual" dans mon client ; que cet objet COM soit implémenté au sein d'une
DLL, d'un EXE ou d'un service.
J'ai inclus ci-dessous les définitions IDL des deux interfaces que j'ai
utilisées pour tous mes tests (elles ne comportent aucune méthode).
Les résultats de ces tests me laissent perplexe : quelque chose du
fonctionnement de COM doit m'échapper. Pourquoi est-ce que mon client
récupère un pointeur NULL sur une interface "custom" implémentée au sein
d'un EXE ou d'un service ?
- Client COM (Win32 Console Application) -
#import "..\ServeurCOM\ServeurCOM.tlb"
using namespace SERVEURCOMLib;
int main(int argc, char* argv[])
{
CoInitialize(NULL);
try {
// Objet avec interface "Custom" :
IObjetCustomPtr pCust(__uuidof(ObjetCustom));
printf("pCust=0x%X", pCust);
// Objet avec interface "Dual" :
IObjetDualPtr pDual(__uuidof(ObjetDual));
printf("pDual=0x%X", pDual);
}
catch (_com_error e) {
printf("%s", e.ErrorMessage());
}
CoUninitialize();
return 0;
}
- Définitions IDL de IObjetCustom et IObjetDual -
[
object,
uuid(18F1C4CF-3693-430A-8170-9733F7528F52),
helpstring("IObjetCustom Interface"),
pointer_default(unique)
]
interface IObjetCustom : IUnknown
{
};
[
object,
uuid(564B7B43-EFBA-45D6-864F-4457540409DD),
dual,
helpstring("IObjetDual Interface"),
pointer_default(unique)
]
interface IObjetDual : IDispatch
{
};

Poser une question


Il manque la définition de la CoClass dans ton IDL : elle s'appelle
comment? C'est son uuid qu'il faut passer au constructeur du
com_ptr_t, pas l'uuid de l'interface.
Dans le doute, vérifies le tlh généré par #import.
Arnaud
Effectivement, les CoClass n'ont été pas mises par l'AppWizard ATL dans le
fichier IDL. Elles se nomment : ObjetCustom et ObjetDual.
Toutefois, elles figurent bien dans le .tlh généré par le #import du client
COM (joint ci-dessous). Et je pense utiliser leur GUID dans le code du
client COM lors des instructions :
IObjetCustomPtr pCust(__uuidof(ObjetCustom));
et
IObjetDualPtr pDual(__uuidof(ObjetDual));
------- ServeurCOM.tlh -------
(...)
//
// Forward references and typedefs
//
struct /* coclass */ ObjetCustom;
struct /* coclass */ ObjetDual;
(...)
//
// Type library items
//
struct __declspec(uuid("a0bbbda8-813f-4f9a-9966-befa55004373"))
ObjetCustom;
// [ default ] interface IObjetCustom
struct __declspec(uuid("18f1c4cf-3693-430a-8170-9733f7528f52"))
IObjetCustom : IUnknown
{};
struct __declspec(uuid("6ea9a117-d352-4af6-a27e-8113acce7567"))
ObjetDual;
// [ default ] interface IObjetDual
struct __declspec(uuid("564b7b43-efba-45d6-864f-4457540409dd"))
IObjetDual : IDispatch
{};
(...)
??? Si elles ne sont pas dans l'IDL, elles ne peuvent pas être dans la
typelibrary et donc encore moins dans le TLH?
Effectivement çà à l'air correct... Qu'et ce que ca donne si tu
essaies d'instancier l'objet "à la main" avec CoCreateInstance?
Arnaud
Rectificatif : les coclass ont bien été mises dans le fichier IDL par le
wizard ATL. J'ai du regarder trop rapidement la dernière fois. Tu avais
raison d'être étonné ;-)
Afin de mieux cerner le problème, j'ai utilisé les fonctions
::CoGetClassObject(), IClassFactory::CreateInstance() et
IUnknown::QueryInterface() pour accéder à mes interfaces (code source en fin
de message).
Le premier appel à IUnknown::QueryInterface() pour récupérer l'interface
IObjetDual ne pose aucun problème.
L'instruction suivante est la seule qui échoue :
hr = pObject->QueryInterface(__uuidof(IObjetCustom),
(void**) &pCust);
Elle retourne un code d'erreur 0x80004002 correspondant au message d'erreur
"Cette interface n'est pas prise en charge".
Avec le débugueur, j'ai remarqué que la création d'un smart pointer
IObjetCustomPtr, échoue à la même étape, l'instruction suivante de
_com_ptr_t d'erreur :
hr = pIUnknown->QueryInterface(GetIID(),
reinterpret_cast<void**>(&m_pInterface));
------- Client COM (Win32 Console Application) -------
#import "..ServeurCOMServeurCOM.tlb"
using namespace SERVEURCOMLib;
int main(int argc, char* argv[])
{
CoInitialize(NULL);
LPCLASSFACTORY pClassFactory;
HRESULT hr;
hr = ::CoGetClassObject(__uuidof(ObjetDual),
CLSCTX_LOCAL_SERVER,
NULL,
IID_IClassFactory,
(void **) &pClassFactory);
if (hr != NOERROR)
return 2;
LPUNKNOWN pObject;
hr = pClassFactory->CreateInstance(NULL,
IID_IUnknown,
(void**) &pObject);
if (hr != NOERROR)
return 3;
IObjetDual* pDual = NULL;
hr = pObject->QueryInterface(__uuidof(IObjetDual),
(void**) &pDual);
if (hr != NOERROR)
return 4;
IObjetCustom* pCust = NULL;
hr = pObject->QueryInterface(__uuidof(IObjetCustom),
(void**) &pCust);
if (hr != NOERROR)
return 5;
pClassFactory->Release();
pObject->Release();
if (pCust != NULL)
pCust->Release();
if (pDual != NULL)
pDual->Release();
CoUninitialize();
return 0;
}