EXCEL Steuerung über COM in C/C++



  • Hallo Forum,

    ich benutze Borland C-Builder (BDS 2006), habe noch eine älteres Projekt, das nur in C und WinAPI entwickelt ist und möchte EXCEL über das COM-Model steuern. Eine Dokumentation über das EXCEL-Objektmodell (VBAXL9.CHM) habe ich, die allerdings nur VB-Beispiele enthält. Ich habe mir folgendes Testprogramm zusammengestellt um diverse Funktionen erstmal ausprobieren zu können. Dabei bin ich auf das erste Problem gestossen und zwar die Funktion "get_Names" des Objekts WorkSheet. Laut "Online Doku" die sich aus dem Include-File "Excel_2k_SRVR.h" ergibt liefert Die Funktion den Typ "NamesPtr" zurück und ich würde erwarten dass ich im Debugger eine Liste meiner in dem Formular definierten Zellennamen bekomme.
    Im Debugger sehe ich aber nichts und nach dem Return der Funktion erfolgt eine Exception; d.h. die Fuktion hat mir den Stack zerschossen.

    Frage:

    gibt es nicht eine entsprechende Dokumentation des EXCEL-Objekt /Methoden Modells in der auch Beispiele für C/C++ enthalten sind, oder kann
    mir jemand sagen, was an der genannten Zuweisung falsch ist?

    Viellen Dank im Voraus

    Code:

    #include   <windows.h>
    #include   <string.h>
    #include   <ctype.h>
    #include   <stdlib.h>
    #include   <stdio.h>
    #include   "Excel_2K_SRVR.h"
    
    //---------------------------------------------------------------------------
    
    VOID	PASCAL EXCEL_Test (HWND hWnd)
    
    {
    TExcelApplication *ExAp;
    TExcelWorkbook  *WB;
    TExcelWorksheet	*WS;
    IDispatch	*ID;
    wchar_t	*Wchr[1],*pWC;
    Variant v;
    BSTR	bst;
    NamesPtr PNam;
    LONG	wbID;
    WORD	Cnt,i;
    
    CoInitialize(NULL);
    
    ExAp = new TExcelApplication(NULL);
    WB = new TExcelWorkbook(NULL);
    WS = new TExcelWorksheet(NULL);
    ExAp->Connect();  wbID = 0;
    ExAp->set_Visible(0, 1);
    v = "D:\\ABB\\WD30\\muster.xls";
    ExAp->Workbooks->Add (v,wbID);
    WB->ConnectTo(ExAp->ActiveWorkbook);
    WS->ConnectTo(WB->ActiveSheet);
    
    pWC = WB->get_Name();
    
    PNam = WS->get_Names();
    
    ExAp->Disconnect();
    Sleep(500);
    ExAp->Quit();
    delete WS;
    delete WB;
    delete ExAp;
    
    CoUninitialize();
    
    }
    


  • Hallo

    Hier im Forum sind bereits viele Threads zum Thema Excel-Anbindung vorhanden, auch die FAQ bietet etwas. Vergleich doch die dortigen Codes mal mit deinem.

    bis bald
    akari



  • Hallo,

    das habe ich gerade nochmal gemacht und zwar mit "EXCEL COM C++"; dann bekomme ich nur meinen eigenen Beitrag als Suchergebnis.

    Außerdem wäre mir die passende Dokumentation der EXCEL-Funktionen die über COM ansprechbar sind wichtiger wie ein Beispiel-Code


  • Mod

    Hallo

    unter der FAQ schon mal geschaut ?
    (Punkt Excel)

    Mfg
    Klaus



  • Hallo,

    habe ich gerade gemacht. Das Problem scheinen auch andere zu haben. Jedenfalls gibt es scheinbar keine Doku für C++.

    http://www.c-plusplus.net/forum/viewtopic-var-t-is-195905-and-highlight-is-texcelapplication.html

    MfG
    Uli



  • Uli schrieb:

    Im Debugger sehe ich aber nichts

    NamesPtr ist ein Interface-Wrapper. Interfaces haben keine Datenelemente, die der Debugger anzeigen könnte, und der konkrete Typ ist ihm natürlich nicht bekannt. Du mußt auf den Inhalt schon über die vom Interface bereitgestellten Methoden zugreifen.

    Uli schrieb:

    und nach dem Return der Funktion erfolgt eine Exception; d.h. die Fuktion hat mir den Stack zerschossen.

    Ein unnötig vorschneller Schluß. Die Exception wird dadurch verursacht, daß du ExAp->Disconnect() und (aus einem Grund, der sich mir nicht erschließt, zeitverzögert) ExAp->Quit() aufrufst, damit die Verbindung zu Excel trennst, jedoch der Smart-Pointer PNam noch auf ein Interface zeigt und im Destruktor dessen Release()-Methode aufzurufen versucht, obgleich es nach der Trennung von Excel nicht mehr verfügbar ist.

    Uli schrieb:

    gibt es nicht eine entsprechende Dokumentation des EXCEL-Objekt /Methoden Modells in der auch Beispiele für C/C++ enthalten sind

    Nein. Bei diesen Header-Dateien handelt es sich lediglich um importierte Type-Libraries (du kannst sie auch bei Bedarf selbst importieren); für die Dokumentation derselben ist der Anbieter, in diesem Falle Microsoft, verantwortlich, und der entschied sich nun, nur VBA-Dokumentation anzubieten.

    Uli schrieb:

    oder kann mir jemand sagen, was an der genannten Zuweisung falsch ist?

    Nein, aber ich kann dir einige andere Fehler benennen:

    VOID    PASCAL EXCEL_Test (HWND hWnd)
    
    {
    TExcelApplication *ExAp;
    TExcelWorkbook  *WB;
    TExcelWorksheet    *WS;
    IDispatch    *ID;
    wchar_t    *Wchr[1],*pWC; // COM-Klassen arbeiten mit BSTR; das ist etwas anderes als wchar_t*.
                              // In C++Builder immer WideString benutzen, nie direkt Stringliterale oder wchar_t-Zeiger!
    Variant v;
    BSTR    bst;
    NamesPtr PNam;
    LONG    wbID;
    WORD    Cnt,i;
    
    CoInitialize(NULL);
    
    ExAp = new TExcelApplication(NULL); // Nicht exception-sicher. Nimm einen RAII-Wrapper (z.B. std::auto_ptr).
    WB = new TExcelWorkbook(NULL);      // Dito.
    WS = new TExcelWorksheet(NULL);     // ...
    ExAp->Connect();  wbID = 0;
    ExAp->set_Visible(0, 1); // Anstelle der set_* und get_*-Methoden kannst du auch direkt die Properties benutzen.
    v = "D:\\ABB\\WD30\\muster.xls";
    ExAp->Workbooks->Add (v,wbID);
    WB->ConnectTo(ExAp->ActiveWorkbook); // Warum nicht einfach den Rückgabewert der darüberstehenden Zeile?
    WS->ConnectTo(WB->ActiveSheet);
    
    pWC = WB->get_Name(); // Der Zeiger ist nach Ablauf dieser Zeile nicht mehr gültig. Nimm einen WideString.
    
    PNam = WS->get_Names();
    
    ExAp->Disconnect();
    Sleep(500); // So etwas ist eigentlich immer falsch.
    ExAp->Quit();
    delete WS; // Siehe obige Anmerkung zu RAII.
    delete WB;
    delete ExAp;
    
    CoUninitialize(); // Auch für so etwas nehme man am besten einen RAII-Wrapper, mindestens aber try/__finally!
    
    }
    


  • NamesPtr ist ein Interface-Wrapper. Interfaces haben keine Datenelemente, die der Debugger anzeigen könnte, und der konkrete Typ ist ihm natürlich nicht bekannt. Du mußt auf den Inhalt schon über die vom Interface bereitgestellten Methoden zugreifen.

    get_Names() ist doch eine Methode des Objekts WorkSheet. Die Frage ist doch wie ich an das Ergebnis komme.

    Ein unnötig vorschneller Schluß. Die Exception wird dadurch verursacht, daß du ExAp->Disconnect() und (aus einem Grund, der sich mir nicht erschließt, zeitverzögert) ExAp->Quit() aufrufst, damit die Verbindung zu Excel trennst, jedoch der Smart-Pointer PNam noch auf ein Interface zeigt und im Destruktor dessen Release()-Methode aufzurufen versucht, obgleich es nach der Trennung von Excel nicht mehr verfügbar ist.

    Das mit dem Release war mir nicht klar. Die Verzögerung war ein Versuch, weil vorher der Disconnect und Quit auch schon nicht richtig funktioniert hat. Dann wäre es wohl besser PNam in diesem Beispiel außerhalb der Funktion zu deklarieren, weil man nicht weiss wann der Release aufgerufen wird.

    Wenn es keine Dokumentation für C++ gibt, ist es wohl einfacher eine DLL in VB zu entwerfen und die dann an mein C++ Programm anzubinden.



  • Uli schrieb:

    NamesPtr ist ein Interface-Wrapper. Interfaces haben keine Datenelemente, die der Debugger anzeigen könnte, und der konkrete Typ ist ihm natürlich nicht bekannt. Du mußt auf den Inhalt schon über die vom Interface bereitgestellten Methoden zugreifen.

    get_Names() ist doch eine Methode des Objekts WorkSheet. Die Frage ist doch wie ich an das Ergebnis komme.

    Das machst du schon richtig. Das Ergebnis ist aber ein Zeiger auf das Interface Excel_2k::Names, und für das gilt, was ich sagte.

    Uli schrieb:

    Das mit dem Release war mir nicht klar. Die Verzögerung war ein Versuch, weil vorher der Disconnect und Quit auch schon nicht richtig funktioniert hat. Dann wäre es wohl besser PNam in diesem Beispiel außerhalb der Funktion zu deklarieren, weil man nicht weiss wann der Release aufgerufen wird.

    Du kannst PNam.Release() auch manuell aufrufen. (Aber bitte nicht PNam->Release()!)

    Uli schrieb:

    Wenn es keine Dokumentation für C++ gibt, ist es wohl einfacher eine DLL in VB zu entwerfen und die dann an mein C++ Programm anzubinden.

    Wenn man die COM-Mechanismen durchblickt, ist es auch in C++ kein Problem. Falls nicht, so ist aber VB etwas weniger fehleranfällig.



  • audacia schrieb:

    Das machst du schon richtig. Das Ergebnis ist aber ein Zeiger auf das Interface Excel_2k::Names, und für das gilt, was ich sagte.

    welche Methode würdest Du denn dann hier nehmen?

    Screenshot



  • Uli schrieb:

    welche Methode würdest Du denn dann hier nehmen?

    Genau das, was man in VBA machen würde: mit der Eigenschaft Count die Anzahl abfragen und mit Item per Index (ab 1!) iterieren.

    // PNam = WS->Names; // das sind vermutlich nicht die gewünschten Namen
        PNam = WB->Names; // das schon eher
        for (int i = 1, c = PNam->Count; i <= c; ++i)
            std::wcout << PNam->Item (TVariant (i))->Name_ << std::endl;
    

Anmelden zum Antworten