Objektübergabe in COM/ATL?



  • Hallo Forum,

    ich habe ein C++ Projekt mit mehreren Klassen zu denen ich eine Com/Atl Schnittstelle erstellen möchte. Einige der Klassen haben Methoden die als Parameter andere Objekte meiner Klassen erfordern. Beispiel:
    1. Die Klasse CPerson hat das ATL Interface IPerson und die ATL Klasse CIPerson.
    2. Die Klasse CFirma hat das ATL Interface IFirma und die ATL Klasse CIFirma. Zusätzlich zu den Methoden die Namen, etc setzen hat IFirma die Methode AddCustomer(CPerson *Customer). Mein Problem: Wie kann ich ein Person über die Schnittstelle transportieren?

    Wenn ich es mit CPerson versuche: CPerson ist nicht definiert. Mache ich #include "CPerson.h" oder die kurzdefinition "class CPerson;" fangen die Probleme erst richtig an. class nicht erkannt, ....

    Ich kann aber AddCustomer(IPerson *Customer) machen. Bloß wie komme ich von IPerson zurück zu CIPerson oder CPerson? CIPerson enthält ja CPerson als Objekt.

    1. Wie übergibt man Objekte über die Schnittstelle? Oder
    2. Gibt es eine Möglichkeit vom Interface zur Klasse zu kommen? Wenn das geht würde ich einfach eine nicht-ATL Methode schreiben die mir das Datenobjekt zurückgibt.

    Vielen Dank

    Bommel



  • So soll es dann in Excel VBA aussehen:

    Function LohnsteuerErklaerung()
       Dim Arbeitnehmer As Person
       Dim UnsereFirma As Company
    
       ' Arbeitnehmer erstellen:
       Call Arbeitnehmer.SetName("Emil Maier")
    
       ' Arbeitnehmer hinzufügen:
       Call UnsereFirma.AddEmployee(Arbeitnehmer)
    
       ' Jetzt wird die Steuerberechnung angestoßen:
       Call UnsereFirma.CalculateTaxes
    
       ' Arbeitnehmer wieder aus der Firma herausholen:
       Arbeitnehmer = UnsereFirma.GetEmployee(1)
    
       ' Hat die Berechnung funktioniert?
       Debug.Print Arbeitnehmer.GetTax()
    End Function
    

    Ich habe ein Problem Objekte wie den Arbeitnehmer über die COM Schnittstelle zu schicken. Läßt sich obiger Code mit einer Com/Atl DLL überhaupt bewerkstelligen?



  • ad 1: du kannst keine Klasse in COM übergeben, nur Interface-Pointer. Die "IPerson*" Sache passt schon so.

    ad 2: ich sehe 2 Möglichkeiten:

    a: mach alles über COM Interfaces. Die kannst du dir über QueryInterface holen, ist also kein Problem da dranzukommen.

    b: caste den Zeiger einfach. Solange IPerson immer nur von CPerson implementiert wird, und sonst von nix, ist das kein Problem. Will aber irgendjemand selbst IPerson implementieren ... wird das Programm an der Stelle vermutlich abstürzen wenn ein "fremdes" IPerson Interface übergeben wird.

    Auf Grund der möglichen Probleme mit (b) würde ich eher zu (a) raten.
    Die "zusätzlichen Funktionen" die du brauchst damit du machen kannst was du machen willst kannst du ja auch gerne in ein "privates" Interface packen, dessen IID und Funktionen nirgends dokumentiert sind -- falls das wünschenswert sein sollte.



  • Hallo Hustbaer,

    was meinst Du genau mit a)? Ich schreibe eigentlich nur einen Wrapper für die Klassen unserer Hauptapplikation damit man sie auch unter Excel VBA benutzen kann. Ich darf CPerson und CCompany nicht verändern.

    Mein momentanes Design sieht so aus:
    Zu CPerson und CCompany wurden die ATL Klassen CAtlPerson und CAtlCompany erstellt. Die Atl Klassen enthalten jeweils als private Member eine Instanz von CPerson und CCompany. Mit Gettern und Settern in den ATL Klassen komme ich an die eigentlichen Klassen.

    Der Setter funktioniert:

    STDMETHODIMP CAtlCompany::AddCustomer(IAtlPerson* Customer) {
    	// m_oCompany ist das Company Objekt das ich nicht verändern darf. Signatur: AddPerson(CPerson Person)
    	m_oCompany.AddPerson(((CAtlPerson *)Customer)->GetObj()); // Der Cast von Hustbaer und dann die priv Person
    
    	return S_OK;
    }
    

    Mein Problem ist der Getter:

    STDMETHODIMP CAtlCompany::GetCustomer(IAtlPerson** Customer) {
    	CAtlPerson *oAtlPerson = new CAtlPerson(); // Eine neuen Wrapper erstellen
    	CPerson oPerson = m_oCompany.GetPerson(); // Die oben eingefügte CPerson wieder herausholen.
    	oAtlPerson.SetObj(oPerson); // Die herausgeholte Person in den Wrapper erstellen.
    	*Customer = oAtlPerson; // Den Wrapper zurückgeben.
    
    	return S_OK;
    }
    

    Ich bekomme die Meldung das CAtlPerson abctract sei. Ich finde keine virtuellen Funktionen, CAtlPerson erbt aber von IAtlPerson. Hier steige ich dann aus:

    __interface IAtlPerson : IDispatch
    {
    	[id(1), helpstring("method SetName")] HRESULT SetName([in] BSTR Name);
    	[id(2), helpstring("method GetName")] HRESULT GetName([out,retval] BSTR* Name);
    };
    

    Kann ich IAtlPerson instanziieren? (Welche Funktionen müsste ich mit Rümpfen versehen, ich kriege immer nur die Meldung "error C2259: Instanz von abstrakter Klasse kann nicht erstellt werde")

    Vermutlich ist das aber ein Holzweg...
    Ich werde ein kleines losgelöstes Testprojekt erstellen und es mal hochladen.




Anmelden zum Antworten