Collections in ATL COM+ DLL
-
Meine Frage richtet sich allgemein an jene, die Erfahrung darin haben, mit C++ eine ATL COM+ DLL aufzubauen, die eine Collection Class enthält.
Meine Frage richtet sich speziell an jene, die das Buch von Richard Grimes "Beginning ATL 3 COM Programming" kennen. Hier wird in Kapitel 9, Seite 410, illustriert wie man mit C++ eine ATL COM+ DLL formuliert, um eine Collection Class einzurichten, die dann beispielsweise in Excel VBA über eine Referenz genutzt werden kann.
Konkretisiert wird dies am Beispiel einer BusinessUnit, die aus diversen Employee Objects besteht: Manager, Sekretärin, Development-team. Das Team wiederum besteht ebenfalls aus Employee Objects. Es gibt also ein DeveloperCollection, deren Items aus Employee Objekten besteht. Jeder Employee hat die Attribute Name und ID.
- In Excel VBA kann man dann entsprechend eine BusinessUnit (BU) aufbauen und nutzen.
- In Excel VBA kann man dann durch die BU.DeveloperCollection mithilfe des "For Each" Befehls durchloopen, um die Employee Attribute auszulesen.
Soweit so gut.Was ich gerne lernen möchte:
Wie kann ich auf der C++ Ebene innerhalb der BusinessUnit.cpp auf die Items der DeveloperCollection zugreifen?
Ich kann hier zwar eine Methode formulieren, um zB. das "Count" Attribut der DeveloperCollection abzurufen (der Count zeigt die Anzahl von Collection-Items an). Aber ich weiss nicht, wie ich die individuellen Items greifbar mache (wahrscheinlich über die Get_Item Methode). Mir fehlen die nötigen C++ Kenntnisse. Mit einer Blaupause käme ich aber weiter!
Die folgenden Versuche aus der CBusinessUnit.cpp geben eine Idee darüber, wie ich momentan scheitere:
void CBusinessUnit::GetDevCol(IDeveloperCollection* pDevs, long& Cnt) { pDevs->get_Count(&Cnt); // dies funktioniert ! IEmployee* pE; VARIANT indx; indx.lVal =1; VARIANT *ZZ; VariantInit(ZZ); VARIANT hr; // ab hier geht nichts mehr... ich versuche irgendwie an die Items ranzukommen // hr=pDevs->QueryInterface(indx.lVal, pE); // hr=CollImpl::get_Item(indx.lVal, ZZ); // CComObject<EnumType>* p; // VARIANT* mm; // hr=pDevs->get__NewEnum(indx.lVal ); }
Gerne kann ich konkreter werden (code snippets etc.), wenn sich jemand prinzipiell auskennt.
Viele Grüße
RC
-
Normalerweise gibt es ein Item-Property bzw. eine Item-Methode am Interface die man dafür verwendet. Musst du dir halt mal die Typelib oder die IDL-Datei der COM-Komponente ansehn.
// Testbeispiel keine MFC-Anwendung sondern // mit Verwendung von #import... CComPtr<IIrgendwas> spItem; // So HRESULT hr1 = pDevs->get_Item(0, &spItem); // oder vielleicht so HRESULT hr2 = pDevs->raw_Item(0, &spItem);
-
Vielen Dank für die Unterstützung, Chew-Z!
In der IDL-Datei der COM-Komponente wird die Item-methode der IDeveloperCollection wie folgt aufgeführt:
interface IDeveloperCollection : IDispatch { [propget, id(0), helpstring("property Item")] HRESULT [b]Item[/b]([in] VARIANT Index, [out, retval] VARIANT *pVal); [propget, id(1), etc... etc. }
Folgende Resultate erhalte ich nun durch die Anwendung des CCompPtrs:
------------------- Versuch 1 --------------------
void CBusinessUnit::GetDevCol(IDeveloperCollection* pDevs, long& Cnt) { CComPtr<[b]IEmployee[/b]> spItem; HRESULT hr1=pDevs->get_Item(0, &spItem); }
*** gibt Fehlermeldung: error C2664: 'get_Item' : cannot convert parameter 1 from 'const int' to 'struct tagVARIANT'
No constructor could take the source type, or constructor overload resolution was ambiguous------------------- Versuch 2 --------------------
void CBusinessUnit::GetDevCol(IDeveloperCollection* pDevs, long& Cnt) { VARIANT indx; indx.lVal =1; CComPtr<[b]VARIANT[/b]> spItem; HRESULT hr1=pDevs->get_Item([b]indx[/b], &spItem); }
*** gibt Fehlermeldung:
error C2664: 'get_Item' : cannot convert parameter 2 from 'struct tagVARIANT ** ' to 'struct tagVARIANT *'
Types pointed to are unrelated; conversion requires reinterpret_cast, C-style cast or function-style cast------------------- Versuch 3 --------------------
void CBusinessUnit::GetDevCol(IDeveloperCollection* pDevs, long& Cnt) { VARIANT indx; indx.lVal =1; CComPtr<VARIANT> spItem; HRESULT hr1=pDevs->get_Item(indx, [b]spItem[/b]); }
*** gibt Fehlermeldung:
error C2039: 'Release' : is not a member of 'tagVARIANT'
c:\programme\microsoft visual studio\vc98\include\oaidl.h(402) : see declaration of 'tagVARIANT'
c:\programme\microsoft visual studio\vc98\atl\include\atlbase.h(443) : while compiling class-template member function '__thiscall ATL::CComPtr<struct tagVARIANT>::~ATL::CComPtr<struct tagVARIANT>(void)'
Error executing cl.exe.Welche Schlüsse und Korrekturen kann ich aus den Meldungen ableiten?
p.s. Ich habe keinen #import in CBusinessUnit.cpp formuliert. Liegt es daran? Wie müsste der aussehen?
-
Ich würde Dir einfach mal raten Dich mit ein paar Basics von ATL auseinanderzusetuen.
Ein VARIANT ist kein CComPTR und kein Interface.void CBusinessUnit::GetDevCol(IDeveloperCollection* pDevs, long& Cnt) { CComVariant index = 1; CComVariant item; HRESULT hr1=pDevs->get_Item(indx, &item); }
PS: For Each Enumerierungen erfolgen mit dem Interface _NewEnum!
Siehe auch IEnumVARIANT!
-
Hallo Martin, dies funktioniert!
Ja, ich begreife ATL noch nicht. Aber dieser Austausch hilft mir zu erkennen, wo ich ansetzen muss. Ist es fair, noch weiter zu fragen?
item stellt ein Employee Object dar (Die Collection besthet aus Employees, siehe ADD Methode in der IDL Datei. Wie komme ich an die Attribute ran, zB. and die EmployeeID (dies ist ein Long-Attribut der Employee Class) ?
Eintrag in der IDL-Datei:
interface IDeveloperCollection : IDispatch { [propget, id(0), helpstring("property Item")] HRESULT Item([in] VARIANT Index, [out, retval] VARIANT *pVal); [propget, id(1), helpstring("property Count")] HRESULT Count([out, retval] long *pVal); [propget, id(DISPID_NEWENUM), helpstring("[b]property _NewEnum[/b]"), restricted] HRESULT _NewEnum([out, retval] LPUNKNOWN *pVal); [id(3), helpstring("method Add")] HRESULT Add([in] [b]IEmployee* pEmployee[/b]); };
Und:
interface IEmployee : IDispatch { [propget, id(1), helpstring("property Name")] HRESULT Name([out, retval] BSTR *pVal); [propput, id(1), helpstring("property Name")] HRESULT Name([in] BSTR newVal); [propget, id(2), helpstring("property EmployeeID")] HRESULT [b]EmployeeID[/b]([out, retval] long *pVal); [propput, id(2), helpstring("property EmployeeID")] HRESULT EmployeeID([in] long newVal); };
Hartes Nachdenken und die Berücksichtigung Deines Hinweises zgl. _NewEnum suggerieren mir:
IUnknown* Emp; HRESULT hr2=pDevs->get__NewEnum(&Emp); HRESULT hr3=Emp->QueryInterface .... wie weiter.. wenn überhaupt?
(..dies ist also eine anderer Ansatz als über die Item Methode -- gerne lerne ich auch wie ich mit der Get_Item Methode die EmployeeID auslesen kann)
Viele Grüße,
Ralph
-
Du bekommst durch get_Item einen Variant. In dem ist der VT_DISPATCH Member gesetzt.
Den musst Du heruasholen undmit QueryInterface Dir einen Zeiger aif IEmployee besorgen. Mit dem kommst Du dann an die Properties.1. Ich bin kein Handuch. Lies mal die Doku nd sample zu CComPtr/CComQIPtr/CComPtr::QueryInterface...
2. Bitte lies mal grundsätzlich was über COM... Was Du hier machst ist stochern im Nebel!
Du mischt hier fürchterlich pures C++ mit COM. Dabei können gerade die ATL COM Klassen Dich hier kräftig unterstützen.
-
ok - danke jedenfalls. Bin jetzt am lesen..
R
-
ok, nach ein bisschen Lektüre kann ich nun auf die individuellen Items der Developer Collection zugreifen, um dann zB. die Summe der EmployeeIDs zu ermitteln. Dabei wird der Ansatz über Import verwendet.
Ganz oben in BusinessUnit.cpp steht#import "C:\RC\VC6\BUObj_1\BUObj_1.tlb" // adjust to your own directory, evtl, make a relative path
In IDL file muss man notieren
interface IBusinessUnit : IDispatch { etc. [id(4), helpstring("method Add_IDs")] HRESULT Add_IDs([out, retval] long *SumOfIDs); }
In BusinessUnit.h muss man notieren
// IBusinessUnitpublic: STDMETHOD(Add_IDs)(/*[out, retva]*/ long *myCnt); etc.
Weiter unten in BusinessUnit.cpp wird die Methode zum Addieren der ID's geliefert:
STDMETHODIMP CBusinessUnit::Add_IDs(long *SumOfIDs) { // TODO: Add your implementation code here ... BUOBJ_1Lib::IDeveloperCollectionPtr pDevCol(__uuidof(BUOBJ_1Lib::DeveloperCollection )); pDevCol=this->m_pDevelopers; long nrEmps; m_pDevelopers->get_Count(&nrEmps); CComVariant indx; BUOBJ_1Lib::IEmployeePtr pEmp(__uuidof(BUOBJ_1Lib::Employee )); long sumIDs=0; for (long i=1; i<=nrEmps; ++i) { indx=i; pEmp=pDevCol->GetItem(indx.lVal); sumIDs = sumIDs + pEmp->EmployeeID ; } *SumOfIDs=sumIDs; return S_OK; }
In Excel VBA kann man dann schreiben:
sub Test_COM_object Dim BU As New BusinessUnit Dim DevCol As New DeveloperCollection Dim EMP As New Employee '--------------------------------- ' Developers '--------------------------------- Set EMP = Nothing EMP.Name = "George" EMP.EmployeeID = 1 BU.Developers.Add EMP Set EMP = Nothing EMP.Name = "Ringo" EMP.EmployeeID = 2 BU.Developers.Add EMP Set EMP = Nothing EMP.Name = "John" EMP.EmployeeID = 3 BU.Developers.Add EMP Set EMP = Nothing EMP.Name = "Paul" EMP.EmployeeID = 4 BU.Developers.Add EMP '--------------------------------- ' Bilde die Summe der EmployeeIDs '--------------------------------- Dim SumID As Long SumID = BU.Add_IDs ' sumID ist 10 (zehn) end sub