Newsgroup: it.comp.lang.c++
Titolo: Client Com/ATL Help

Ho costruito una ActiveX-dll (un componente senza apparenza visuale, di cui
sotto riporto il .idl) utilizzando VC++5.0 e ATL
1) L’utilizzo del componente  risulta banale in VB5, basta utilizzare il
menu Project/References... e si ha la visibilita’
delle classi, property, metodi in esso contenuti.
2)  Con il C++Builder, sono riuscito ad interagire con le classi esportate
dalla .dll con un codice del tipo:
/*******************/
Variant prova2;
prova2 = Variant::CreateObject("DBHLayer.Prova2");
Variant prova1 = prova2.OlePropertyGet("Prova");
prova1.OlePropertySet("Number", Variant(2) );
prova1.OleProcedure("NumberX", Variant(4) );
Variant val =prova1.OlePropertyGet("Number");
/*******************/
Domanda:
Non c’e’ niente di meglio?
Non che ami VB ma almeno sfrutta l’automazione per suggerirmi cosa posso
fare
Domanda:
Come  faccio a intercettare gli eventi Fire e FireStr generati da Prova2
attraverso la ‘dispinterface _EventGenerator’
3)  Se con il C++Builder le cose non mi sembrano molto belle, mi par di
capire che con il VC++, sia più facile costruire oggetti di tal tipo che non
utilizzarli !
Domanda:
devo proprio fare un .ocx (anche se non mi serve) per poter avere un po’ di
supporto dall’ambiente di sviluppo?
Riporto la dichiarazione delle interfacce contenuta nell’.idl
----------------------------------------------------------------------------
----------------------------
// DBHLayer.idl : IDL source for DBHLayer.dll
//
// This file will be processed by the MIDL tool to
// produce the type library (DBHLayer.tlb) and marshalling code.
import "oaidl.idl";
import "ocidl.idl";
/////////////////
 [
  object,
  uuid(32804C9D-B468-11D2-A875-00105A5B4A71),
  dual,
  helpstring("IProva Interface"),
  pointer_default(unique)
 ]
 interface IProva : IDispatch
 {
  [id(1), helpstring("method NumberX")] HRESULT NumberX(int x);
  [propget, id(2), helpstring("property Number")] HRESULT Number([out,
retval] long *pVal);
  [propput, id(2), helpstring("property Number")] HRESULT Number([in] long
newVal);
 };
/////////////////
 [
  object,
  uuid(32804CA1-B468-11D2-A875-00105A5B4A71),
  dual,
  helpstring("IProva2 Interface"),
  pointer_default(unique)
 ]
 interface IProva2 : IDispatch
 {
  [id(1), helpstring("Generate an error")] HRESULT GenerateError();
  [propget, id(2), helpstring("property Prova")] HRESULT Prova([out, retval]
IProva** pVal);
  [propget, id(3), helpstring("property Name")] HRESULT Name([out, retval]
BSTR *pVal);
 };
/////////////////
[
 uuid(32804C90-B468-11D2-A875-00105A5B4A71),
 version(1.0),
 helpstring("DBHLayer 1.0 Type Library")
]
library DBHLAYERLib
{
 importlib("stdole32.tlb");
 importlib("stdole2.tlb");
 [
  uuid(32804C9E-B468-11D2-A875-00105A5B4A71),
  helpstring("Prova Class")
 ]
 coclass Prova
 {
  [default] interface IProva;
 };
 [
  uuid(32804CA3-B468-11D2-A875-00105A5B4A71),
  helpstring("EventGenerator Interface for prova2")
 ]
 dispinterface _EventGenerator
 {
  properties:
  methods:
  [id(1)] HRESULT Fire( int x );
  [id(2)] HRESULT FireStr( BSTR str );
 };
 [
  uuid(32804CA2-B468-11D2-A875-00105A5B4A71),
  helpstring("Prova2 Class")
 ]
 coclass Prova2
 {
  [default] interface IProva2;
  [default, source] dispinterface _EventGenerator;
 };
};
Grazie Davide
d...@menhir-informatica.com

supporto dall’ambiente di sviluppo?
No, non necessariamente (se usi versioni aggiornate di VC++, almeno:-).
Gestire eventi dal tuo codice cliente richiede un minimo piu` di
sforzo che non chiamare metodi e leggere/scrivere proprieta`.
Devi implementare almeno una micro-versione di IDispatch
(supponendo che la source interface sia, come usa, una
dispinterface); ai vari eventi, ne e` chiamata la Invoke.  Alcuni
frammenti di esempio:
// una tipica micro-versione di IDispatch: l'uuid e` quello della
// dispinterface che interessa
struct __declspec(uuid("0856178f-bfa3-11d2-b08c-db26602eca62"))
evt: public IDispatch
{
    /* IUnknown methods */
    STDMETHOD(QueryInterface)(REFIID riid, LPVOID FAR* ppvObj) {
        if(riid==IID_IUnknown || riid==IID_IDispatch || riid==__uuidof(evt))
{ 
            *ppvObj = this; return S_OK; 
        }
        return E_NOINTERFACE;
    }
    STDMETHOD_(ULONG, AddRef)() { return 2; }
    STDMETHOD_(ULONG, Release)() { return 1; }
    /* IDispatch methods */
    STDMETHOD(GetTypeInfoCount)(UINT FAR* pctinfo) { *pctinfo=0; return S_OK; }
    STDMETHOD(GetTypeInfo)(UINT itinfo, LCID lcid, ITypeInfo FAR* FAR* pptinfo) {
        return E_NOTIMPL;
    }
    STDMETHOD(GetIDsOfNames)(REFIID riid, OLECHAR FAR* FAR* rgszNames, UINT cNames,
        LCID lcid, DISPID FAR* rgdispid) {
            return E_NOTIMPL;
    }
    STDMETHOD(Invoke)(DISPID dispidMember, REFIID riid, LCID lcid,
      WORD wFlags, DISPPARAMS FAR* pdispparams, VARIANT FAR* pvarResult,
      EXCEPINFO FAR* pexcepinfo, UINT FAR* puArgErr);
};
// in questo esempio la dispinterface ha un solo metodo, con un [in]long
// come argomento:
STDMETHODIMP evt::Invoke(DISPID dispidMember, REFIID riid, LCID lcid,
    WORD wFlags, DISPPARAMS FAR* pdispparams, VARIANT FAR* pvarResult,
    EXCEPINFO FAR* pexcepinfo, UINT FAR* puArgErr)
{
    variant_t argom(pdispparams->rgvarg[0]);
    long l = argom;
    std::cout << "Ping(" << l << ")\n";
    return S_OK;
}
Un uso tipico potrebbe essere (per una coclass Piat, con una
interfaccia IPiat con un metodo Pingy che prende un intero,
e 
una source dispinterface come appena accennato):
void fa()
{
    evt ev;
    IPiatPtr p(__uuidof(Piat));
    IConnectionPointContainerPtr picpc(p);
    IConnectionPointPtr picp;
    picpc->FindConnectionPoint(__uuidof(evt), &picp);
    DWORD dwCookie;
    picp->Advise(&ev, &dwCookie);
    p->Pingy(23);
    p->Pingy(45);
    picp->Unadvise(dwCookie);
}
Fortunatamente, il noioso codice boilerplate per la
dispinterface e` sostanzialmente sempre lo stesso,
quindi non e` difficile scriverlo una volta e riusarlo
tante volte.  La tipica sequenza di costruzione
dell'IConnectionPointContainerPtr, chiamata alla
sua FindConnectionPoint, ecc ecc sino all'Unadvise,
e` similmente facile da incapsulare in un oggettino
e relativo dtor.  Peccato che VC++ non le offra gia`
precotte, almeno che io sappia (non senza usare
l'ATL per generarle, intendo), ma stiamo comunque
parlando di poche righe.
Alex