String aus C++ dll an VBA übergeben



  • Falls dieser Thread mal gesucht wird, möchte ich abschließend noch berichten, wie ich das Problem letztlich gelöst habe (früher war es viel einfacher):
    Der rückzugebende String muß als Parameter übergeben werden und zuvor in VBA mit Nullen vorgefüllt werden. Zur Vermeidung von Speicherproblemen wird die Länge des vorgefüllten Strings von VBA aus übergeben und in C++ ggf. auf dieses Maß gekürzt. Die Funktion, die den String manipuliert gibt als Ergebnis einen Integer-Wert zurück, der die Länge des Ergebnis-Strings ausgibt. In VBA wiederum wird der manipulierte String durch die Left-Funktion auf diese Länge abgeschnitten, da er sonst rechts weitere Nullen enthält.

    Im BCB:

    extern  "C" __declspec(dllexport) __stcall int CProgramm (int Pufferlaenge, char* VBVariable);
    
    char* CProgramm (int Pufferlaenge, char* VBVariable)
    {
    AnsiString EndErgebnis = „Ergebnis“;
    int Laenge = EndErgebnis.Length();
      //wenn das Ergebnis länger ist als die übergebene Pufferlänge wird das Ergebnis entsprechend gekürzt
      if (Laenge > Pufferlaenge - 1)
       {
       EndErgebnis.Delete (Pufferlaenge, EndErgebnis.Length() - Pufferlaenge);
       Laenge = EndErgebnis.Length();
       }
      strcpy (VBVariable, EndErgebnis.c_str()); //das Ergebnis in den übergebenen String schreiben
      return Laenge; //die Länge des Strings als Integer zurückgeben
    }
    

    In VBA:

    Private Declare Function CProgramm Lib "MeineDLL.DLL" (ByVal Pufferlaenge As Integer, ByVal VBAString As String) As Integer
    
    Function CFunktion (sUebergabesgtring As String) As String
     Dim iErgebnislaenge As Integer
     Dim iPuffer As Integer
     iPuffer = 1500 'Höchstmögliche Länge des Strings
     sUebergabestring = String$(iPuffer, vbNullChar) 'den String  mit Nullen vorfüllen
     iErgebnislaenge = CProgramm (iPuffer, sUebergabestring) 'gibt als Resultat die Stringlänge zurück
     sUebergabestring = Left(sUebergabestring, iErgebnislaenge)  'rechts die nicht benötigten Zeichen abschneiden
    CProgramm = sUebergabstring
    End Function
    


  • char* als Rückgabetyp ist wohl falsch 😉

    Früher war gar nichts einfacher, das war schon immer so, schon zu VB3.0 und Turbo C++ 😮 Wenn man in Speicher schreibt der einem nicht gehört macht es immer BUMM.



  • Danke, war ein Tippfehler, hab es korrigiert.
    Früher war es doch einfacher. Die C++ Funktion hat ein char* zurückgegeben und selbst für die Speicherreservierung gesorgt. Es handelt sich ja nur um einen Pointer auf eine Zeichenkette. Diese Funktion in VBA importiert konnte dort das Ergebnis String liefern. Das hat noch in VB6 und in VBA bis Office 2003 so gut funktioniert.



  • W. Posur schrieb:

    Danke, war ein Tippfehler, hab es korrigiert.
    Früher war es doch einfacher. Die C++ Funktion hat ein char* zurückgegeben und selbst für die Speicherreservierung gesorgt. Es handelt sich ja nur um einen Pointer auf eine Zeichenkette. Diese Funktion in VBA importiert konnte dort das Ergebnis String liefern. Das hat noch in VB6 und in VBA bis Office 2003 so gut funktioniert.

    Sicher nicht, wenn das bei Dir so funktioniert hat, dann war das Zufall.
    Dann hsat Du keine variable langen Strings in VBA sondern fixlange Strings verwendet.



  • An Zufall glaube ich nicht. Es funktioniert ja immer noch mit Strings variabler Länge in älteren VBA-Versionen sowie in OpenOffice und LibreOffice neuen Datums. Stringlängen über 65000 habe ich allerdings nicht ausprobiert. Warum sollte ein vernünftiges Basic auch nicht in der Lage sein, zu erkennen, daß es von einer externen Funktion einen Zeiger auf eine Null-terminierte Zeichenfolge bekommt und diese in einen Basic-String umzuwandeln ?
    Wie auch immer, ich vermute mal, die Änderungen im VBA sind durch die Unicode Funktionalität verursacht.
    Mittels weiterer Recherche habe ich jetzt doch noch eine viel elegantere Methode gefunden, damit VB und VBA einen String bekommen. Ich liefere eine BSTR anstatt einen char* zurück, damit kommt auch das neue VBA klar.

    Im BCB:

    extern  "C" __declspec(dllexport) BSTR _stdcall CProgramm (); 
    
    BSTR _stdcall CProgramm () 
    { 
    AnsiString EndErgebnis = „Ergebnis“;  //das ist der AnsiString, den meine Funktion ermittelt hat und nun an VBA liefern soll 
    BSTR BErgebnis; // diesen BSTR String wird die Funktion zurückgeben 
    int iLaenge = EndErgebnis.Length(); //die Länge des Ergebnis-AnsiStrings wird ermittelt 
    
    LPSTR strSrc = (LPSTR)EndErgebnis.c_str(); //Umwandlung Ergebnis in LPSTR
    LPSTR strDst = (LPSTR) BErgebnis; //dito für Ausgabestring
    for(int i=0; i<= iLaenge; i++)
     *strDst++ = *strSrc++; //Umkopieren
    
    return BErgebnis; //der Ergebnisstring vom Typ BSTR wird zurückgeliefert 
    }
    

    Und in VBA ist es jetzt wieder ganz einfach:

    Private Declare Function CProgramm Lib "MeineDLL.DLL" () As String
    


  • Du vergleichst Äpfel mit Birnen 😃

    BSTR ist etwas ganz anderes als char*



  • Nun ja.
    Mein Thema war: Wie schaffe ich es, mit dem BCB eine dll zu schreiben, die einen String zurückgibt, mit dem VBA auch bei neueren Office Versionen etwas anfangen kann.
    Ich habe das Problem für mich gelöst.
    Ich bedanke mich für die konstruktiven Beiträge.



  • hallo,

    habe ein ähnliches problem. teste zunächst mit einer double variable, später soll es mal ein array werden.

    also meine Test-Funktion sieht folgendermaßen aus:

    extern  "C" __declspec(dllexport) double _stdcall square(double & x);
    
    double _stdcall square(double & x)
    {
    	return x*x;
    }
    

    dann braucht man wohl noch ein def-File:

    LIBRARY "square"
    EXPORTS
    square
    

    dann muss man das ganze noch verlinken. hab ich nach dieser anleitung gemacht:
    http://www.forexfactory.com/showthread.php?p=5250053#post5250053

    In VBA dann:

    Declare Function square _
    Lib "E:\C++\dll_datei\Release\square.dll" (ByRef x As Double) As Double
    

    leider läuft es dann in VBA nicht. Die Function taucht zwar auf, aber es erscheint "#WERT!" in der Zelle... Hab ich nen Fehler gemacht?



  • ootobbyoo schrieb:

    dann braucht man wohl noch ein def-File:

    Beim Builder eigentlich nicht, die Beispiele oben mit def-File waren für VC++.

    ootobbyoo schrieb:

    Hab ich nen Fehler gemacht?

    Ich denke "ByRef x as Double" entspricht einem "double* x", glaube nicht, dass VB C++ Referenzen unterstützt.



  • nn schrieb:

    ootobbyoo schrieb:

    dann braucht man wohl noch ein def-File:

    Beim Builder eigentlich nicht, die Beispiele oben mit def-File waren für VC++.

    mh, also ohne def-File läuft es auch nicht. Programmiere mit ecplise und compiliere dann mit dem TDM GCC für 64 bit (http://tdm-gcc.tdragon.net/download).

    Leider auch keine Änderung, wenn ich das "ByRef" weg lasse.

    Ich habe die Funktion in VBA mal in eine Sub eingebaut, da kommt komischerweise die Fehlermeldung:

    "Lautzeitfehler '48':
    Datie nicht gefunden: E:\C++\dll_datei\Release\square.dll"

    Scheint also, als ob er die dll gar nicht erkennt. Da ist sie aber auf jeden Fall...



  • ootobbyoo schrieb:

    Programmiere mit ecplise und compiliere dann mit dem TDM GCC für 64 bit (http://tdm-gcc.tdragon.net/download).

    ...

    Scheint also, als ob er die dll gar nicht erkennt. Da ist sie aber auf jeden Fall...

    Also die Beispiele hier waren für den C++ Builder und 32-Bit.

    Mit 64-Bit Excel habe ich sowas noch nie gemacht, dass ein 32-Bit Excel keine 64-Bit DLL lädt ist klar. In 64-Bit Code sollte doch schon mal __stdcall überflüssig sein ?



  • ALSO,

    erstmal ist es korrekt, dass man mit Excel 32 bit keine 64 bit dll aufrufen kann. Danke für den Hinweis. Der GCC kann aber mit dem Befehl "-m32" auch auf 32 bit comilieren.

    Dann muss der Name der Function in VBA und der Name der Function in C++ gleich sein!!! Und dann war wohl noch an der Syntax was falsch.

    Hab auf jeden Fall eine Lösung gefunden, die dann so aussieht:

    function.cpp:

    extern "C" __declspec(dllexport) double __stdcall square(double x)
    {
        return x*x;
    }
    

    deffile.def:

    LIBRARY "square"
    EXPORTS
    square
    

    Compiler & Linker:

    g++ -m32
    

    und in VBA dann

    Option Explicit
    
    Declare Function square Lib "E:\C++\dll_datei\Release\dll_datei.dll" (ByVal d As Double) As Double
    
    Sub x()
       Dim zw As Double
       zw = square(10)
       MsgBox zw
    End Sub
    

    Und jetzt das ganze mit Arrays...


Anmelden zum Antworten