c++ Dll Array an VBA



  • ps:
    Es wäre vermutlich besser ein SAFEARRAY aus VARIANTs zu machen, wo dann jeder VARIANT vom Typ VT_BSTR ist.

    Siehe Kommentar von Martin Richter hier:
    http://www.c-plusplus.net/forum/p2331792#2331792

    BSTR* dataPtr muss dann zu VARIANT* dataPtr werden, entsprechend auch der Cast angepasst und natürlich die Stelle wo der String reingeschrieben wird entsprechend angepasst.

    Solltest du selbst hinbekommen.

    ps2: Und natürlich VT_SAFEARRAY | VT_VARIANT statt VT_SAFEARRAY | VT_BSTR .



  • Hallo,

    danke für die Antworten. Ich probiere die gerade aus. Eine adere Frage mein dll macht ein Datenbankabfrage. Gibt es keine einfachere Möglichkeit die Daten an VBA zu senden als VARIANT?



  • Ah Ok dann muss ich wohl mich mit VARIANT und safearrays auseinander setzen. Danke erstmal



  • Ne, viel einfacher wird es nicht.
    Du kannst höchstens noch alles in einen String packen. Dann musst du es auf der VB Seite aber wieder auseinanderbasteln. Das ist auf der VB Seite dann umständlich. Und vermutlich auch recht langsam.



  • Wie müsste ich out_result deklarieren, wenn es nicht eine Parameter meiner Funktion wäre?

    VARIANT out_result;
    VARIANT* out_result1 = new VARIANT [?];
    


  • VARIANT result;
    

    ?



  • hustbaer schrieb:

    ATLASSERT(dataPtr[i] == 0);

    Ich kann die ATL Klassen nicht einbinden. Gibt es hierfür eine andere Möglichkeit?



  • dataPtr[i] = SysAllocString(str);
    

    zeigt Fehler. Außerdem ist unser ZielArray ja out_result.
    Wo schreiben wir die Werte den in unserem ZielArray? Oder habe ich das falsch verstanden



  • std::vector<std::wstring> strings;
        strings.push_back(L"A");
        strings.push_back(L"BB");
        strings.push_back(L"CCC");
        strings.push_back(L"DDDD");
    
        // Safearray Bounds vorbereiten
    
        SAFEARRAYBOUND bound = {};
        bound.lLbound = 0;                  // Index des 1. Elements -- muss nicht 0 sein, manche Sprachen verwenden auch gerne 1
        bound.cElements = strings.size();   // Anzahl Elemente
    
        // Safearray erzeugen
    	VARIANT result;
    	result.vt = VT_SAFEARRAY| VT_BSTR;
    	result.parray = SafeArrayCreate(VT_BSTR, 1, &bound);
    
    	void HUGEP* untypedDataPtr = 0;
        HRESULT hr = SafeArrayAccessData(result.parray, &untypedDataPtr);
    
    	if (SUCCEEDED(hr))
        {
    		VARIANT* dataPtr = static_cast<VARIANT*>(untypedDataPtr);
    		// Strings ins Array kopieren
    		BSTR tstBSTR;
            hr = S_OK;
    		long ini = 0;
    
            for (size_t i = 0; i < strings.size(); i++)
            {
    
    			 wchar_t const* str = strings[i].c_str();
    			 tstBSTR = SysAllocString(str);
    			 SafeArrayPutElement(result.parray, &ini, &tstBSTR);
    			 ini++;
    
    		}
    	}
    

    Was ist an diesem Code falsch? Habe ich noch ein Denkfehler?



  • Wenn du SafeArrayPutElement() sollte das SafeArrayAccessData() weg.

    Und natürlich

    ich schrieb:

    ps2: Und natürlich VT_SAFEARRAY | VT_VARIANT statt VT_SAFEARRAY | VT_BSTR.

    Und dann ist ein BSTR trotzdem noch kein VARIANT.



  • So ähnlich vielleicht (wieder ungetestet):

    std::vector<std::wstring> strings; 
    strings.push_back(L"A"); 
    strings.push_back(L"BB"); 
    strings.push_back(L"CCC"); 
    strings.push_back(L"DDDD"); 
    
    // Safearray Bounds vorbereiten 
    
    SAFEARRAYBOUND bound = {}; 
    bound.lLbound = 0;                  // Index des 1. Elements -- muss nicht 0 sein, manche Sprachen verwenden auch gerne 1 
    bound.cElements = strings.size();   // Anzahl Elemente 
    
    // Safearray erzeugen 
    VARIANT result; 
    result.vt = VT_SAFEARRAY| VT_VARIANT; 
    result.parray = SafeArrayCreate(VT_VARIANT, 1, &bound); 
    
    // Strings ins Array kopieren 
    HRESULT hr = S_OK; 
    
    for (size_t i = 0; i < strings.size(); i++) 
    { 
        wchar_t const* str = strings[i].c_str(); 
    
        VARIANT varStr;
        VariantInit(&varStr);
        varStr.bstr = SysAllocString(str);
        if (str && !varStr.bstr)
        {
            hr = E_OUTOFMEMORY;
            break;
        }
        varStr.vt = VT_BSTR;
    
        long index = i; 
        hr = SafeArrayPutElement(result.parray, &index, &varStr); 
        VariantClear(&varStr);
    
        if (FAILED(hr))
            break;
    } 
    
    //...
    

    Wobei ich es wirklich mit Lock/Unlock machen würde, ist im Endeffekt einfacher (und hat weniger Overhead).

    EDIT: Unfug im Code korrigiert.



  • Hallo,

    danke erstmal das Du so schnell antwortest das Hilft sehr. Also wenn ich Access Data wegnehme dann bekomme ich ein Fehlermeldung.

    std::vector<std::wstring> strings;
        strings.push_back(L"A");
        strings.push_back(L"BB");
        strings.push_back(L"CCC");
        strings.push_back(L"DDDD");
    	strings.push_back(L"EEEEE");
    	strings.push_back(L"FFFFFF");
        // Safearray Bounds vorbereiten
    
        SAFEARRAYBOUND bound = {};
        bound.lLbound = 0;                  // Index des 1. Elements -- muss nicht 0 sein, manche Sprachen verwenden auch gerne 1
        bound.cElements = 20;   // Anzahl Elemente
    
        // Safearray erzeugen
    	VARIANT result;
    	result.vt = VT_BSTR| VT_ARRAY;
    	result.parray = SafeArrayCreate(VT_BSTR, 1, &bound);
    
    	void HUGEP* untypedDataPtr = 0;
        HRESULT hr = SafeArrayAccessData(result.parray, &untypedDataPtr);
    
    	if (SUCCEEDED(hr))
        {
    		VARIANT* dataPtr = static_cast<VARIANT*>(untypedDataPtr);
    		// Strings ins Array kopieren
    		BSTR tstBSTR;
            hr = S_OK;
    		long ini = 0;
    
            for (int i =0; i<5;i++)
            {
    
    			// wchar_t const* str = strings[i].c_str();
    			 //tstBSTR = SysAllocString(str);
    			 dataPtr [i].bstrVal = constCharToBSTR("test");
    			 SafeArrayPutElement(result.parray, &ini, &dataPtr);
    			 ini = ini +1;
    			// wcout << SafeArrayGetElement(dataPtr[i],&ini,&tstBSTR);
    		}
    	}
    

    Schreibt der mir die Strings in meinem Array aber immer 4 Positionen später. Die restlichen Felder bekommen schlechtes Ptr



  • Oha ja, hatte den Code nicht durch den Compiler laufen lassen, und übersehen dass das if (SUCCEEDED(hr)) natürlich nicht ohne die Variable hr geht (und auch keinen Sinn macht).

    Hab den Code entsprechend korrigiert.



  • Hallo,

    danke für die Antworten hustbaer. Es funktioniert mit dem Code. Nun die nächste Frage: Wie bekomme ich jetzt ein zwei Dimensionalen Array in meinem VARIANT Result. Ich dachte, ich könnte noch ein zweite for Schleife durchlaufen,
    so das ich in jede Reihen alle Werte ein Mal stehen hätte
    Also:

    std::vector<std::wstring> strings;
    strings.push_back(L"A");
    strings.push_back(L"BB");
    strings.push_back(L"CCC");
    strings.push_back(L"DDDD");
    
    // Safearray Bounds vorbereiten
    
    SAFEARRAYBOUND bound = {};
    bound.lLbound = 0;                  // Index des 1. Elements -- muss nicht 0 sein, manche Sprachen verwenden auch gerne 1
    bound.cElements = strings.size();   // Anzahl Elemente
    
    // Safearray erzeugen
    VARIANT result;
    result.vt = VT_ARRAY| VT_VARIANT;
    result.parray = SafeArrayCreate(VT_VARIANT, 2, &bound);
    
    //BSTR erzeugen
     VARIANT varStr;
     VariantInit(&varStr);
    
    // Strings ins Array kopieren
    HRESULT hr = S_OK;
    
    for (size_t i = 0; i < strings.size(); i++)
    {
    	for (size_t j = 0; j < strings.size(); j++)
    	{
    		wchar_t const* str = strings[[b]j[/b]].c_str();
    
    		varStr.bstrVal = SysAllocString(str);
    		if (str && !varStr.bstrVal)
    		{
    			hr = E_OUTOFMEMORY;
    			break;
    		}
    		varStr.vt = VT_BSTR;
    
    		long index[] = {i,j};
    		hr = SafeArrayPutElement(result.parray, index, &varStr);
    
    		if (FAILED(hr))
    			break;
    	}
    }
    	VariantClear(&varStr);
    

    Aber hier bekomme ich FAILD (hr).
    Was mache ich den hier falsch



  • SAFEARRAYBOUND -> SAFEARRAYBOUND[2]



  • AH Danke habe ich vergessen auch anzupassen. Danke für den Tip. Mache ich gleich.



  • Und das VariantClear(&varStr); gehört auch mit in die innere Schleife, dorthin wo es in meinem Code auch steht.
    Behaupte ich mal.
    Bin mir nicht 100% sicher ob man hier überhaupt VariantClear aufrufen soll, aber ich verstehe die Doku so dass SafeArrayPutElement eine "deep copy" des VARIANT macht, und dann gehört der alte natürlich freigegeben -- weil's sonst leakt.



  • Und die äussere Schleife gehört noch verlassen wenn's ein Problem gegeben hat. Mit beiden Änderungen dann:

    for (size_t i = 0; i < strings.size(); i++) 
    { 
        for (size_t j = 0; j < strings.size(); j++) 
        { 
            wchar_t const* str = strings[j].c_str(); 
    
            varStr.bstrVal = SysAllocString(str); 
            if (str && !varStr.bstrVal) 
            { 
                hr = E_OUTOFMEMORY; 
                break; 
            } 
            varStr.vt = VT_BSTR; 
    
            long index[] = {i,j}; 
            hr = SafeArrayPutElement(result.parray, index, &varStr); 
            VariantClear(&varStr);
    
            if (FAILED(hr)) 
                break; 
        } 
        if (FAILED(hr)) 
            break; 
    }
    

    Den Test ob SafeArrayCreate funktioniert hat hast du auch rausgenommen, der gehört auch wieder rein.
    Und wieso du varStr aus der Schleife rausgezogen hast versteh' ich auch nicht ganz. Immer so lokal wie möglich.



  • Hallo,

    danke das hat sehr gut funktioniert.



  • Hallo,

    eine Frage noch mit Safearray funktioniert es jetzt Perfekt.

    ich erstelle ein DLL, wenn ich diese dann in VBA aufrufe funktioniert es auch.
    Wenn ich aber z.B. Folgende Funktion noch mitrein packe:

    #include "libpq-fe.h"
    ...
    /* Close connection to database */
    void CloseConn(PGconn *conn)
    {
        PQfinish(conn);
      getchar();
        exit(1);
    }
    

    Dann bekomme ich in VBA die Laufzeitfehler 53, dass mein DLL nicht gefunden wird. woran kann das liegen?


Anmelden zum Antworten