c++ Dll Array an VBA



  • Hallo,

    ich habe leider lange nichts mehr mit c++ gemacht. Nun möchte ich eine Dll erstellen, die string array an vba zurückgeben möchte.

    Meine Frage was für ein Datentyp muss ich benutzen?
    Ich suche seit ca. eine Woche nach ein Lösung aber leider nichts gefunden!



  • safearray



  • Hallo,

    danke für dein Antwort. Gibt es vielleicht ein Beispiel? Also Problem 1 Wie bekomme ich den String Array in Safearray. Problem2 Wie bekomme ich es dann nach VBA gesendet?



  • Wie kann man den ein zwei demensionale Array in einem Variant einlesen?



  • borhan schrieb:

    Gibt es vielleicht ein Beispiel?

    Ziemlich sicher, ich hab aber keins zur Hand.

    Also Problem 1 Wie bekomme ich den String Array in Safearray.

    Google es dir.

    Problem2 Wie bekomme ich es dann nach VBA gesendet?

    Dazu wirst du ein COM Objekt erstellen müssen. Ich dachte dass du den Teil schon hast, dass hier ein COM Objekt benötigt wird ist ja nicht vom Datentyp den man zurückgeben möchte abhängig.
    Und bevor du fragst wie das geht...: Google es dir.



  • Danke für dein Antwort. Ich suche seint ca. eine Woche ich habe schon mit google versucht



  • Ich hoffe mal du verwendest Visual Studio. Dann kannst du die ATL Klassen sowie den Wizard für COM Projekte von Visual Studio verwenden.

    Dazu gibt es auch einige Tutorials. Eines von vielen:

    http://www.codeproject.com/Articles/38254/A-Beginner-Tutorial-for-Writing-Simple-COM-ATL-DLL

    Den "and Using it with .NET" Teil ignorierst du dabei einfach.



  • Hallo,

    vielen DAnk erstmal für dein Antwort. Den Teil 2 mit verbinden habe ich schon. Allerdings mit BSTR oder Zahlen, aber mit Variant funktioniert das nicht so richtig. Mein Problem ist eigentlich hauptsächlich die erste Frage. ICh benutze zu Hause MS Visual Studio aber bei der Arbeit Visual Studio Express und da hat man leider den ALT PAcket nicht



  • So in der Art sollte es gehen (ungetestet):

    HRESULT Klasse::Funktion(VARIANT* out_result)
    {
    	// Erstmal sicherstehhen dass alle Output-Parameter nen Wert haben der a) gültig ist und b) nicht freigegeben werden muss
    
    	if (out_result)
    		VariantInit(out_result);
    
    	if (!out_result) // Kein Output Parameter übergeben -> Fehler
    		return E_POINTER;
    
    	// Die Strings die wir als Safearray zurückgeben wollen
    
    	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
    
    	V_ARRAY(out_result) = SafeArrayCreate(VT_BSTR, 1, &bound); // 1D String Array
    	if (!V_ARRAY(out_result))
    	{
    		// Es wurde noch nichts angefordert -> NICHT VariantClear aufrufen sondern einfach VariantInit
    		VariantInit(out_result);
    		return E_OUTOFMEMORY;
    	}
    
    	out_result->vt = VT_SAFEARRAY | VT_BSTR;
    
    	// Zeiger auf die Elemente holen
    
    	void HUGEP* untypedDataPtr = 0;
    	HRESULT hr = SafeArrayAccessData(V_ARRAY(out_result), &untypedDataPtr);
    	if (SUCCEEDED(hr))
    	{
    		BSTR* dataPtr = static_cast<BSTR*>(untypedDataPtr);
    
    		// Strings ins Array kopieren
    
    		hr = S_OK;
    		for (size_t i = 0; i < strings.size(); i++)
    		{
    			ATLASSERT(dataPtr[i] == 0); // Das von SafeArrayAccessData() erzeugte Array sollte aus lauter NULL Strings bestehen...
    
    			wchar_t const* str = strings[i].c_str();
    			// Bei std::wstring ist immer garantiert dass str != 0.
    			// In Fällen wo str 0 sein könnte müssen wir diesen Fall aber behandeln, da SysAllocString(0) wieder 0 zurückgibt, 
    			// was in dem Fall KEIN Fehler ist.
    			if (str != 0)
    			{
    				dataPtr[i] = SysAllocString(str);
    				if (dataPtr[i] == 0)
    				{
    					hr = E_OUTOFMEMORY;
    					break;
    				}
    			}
    		}
    
    		// Element-Zeiger wieder "freigeben"
    		SafeArrayUnaccessData(V_ARRAY(out_result));
    	}
    
    	// Fehler behandeln die bei SafeArrayAccessData() bzw. beim Kopieren der Strings passiert sind
    
    	if (FAILED(hr))
    	{
    		// Wenn wir Fehler zurückmelden müssen alle output Parameter nen gültigen Wert haben, der aber nicht freigegeben werden muss
    		// -> VariantClear
    		// (VariantClear kann SafeArrays freigeben, und kümmert sich ggf. auch um die BSTRs darin -- wir müssen hier also nix "zu Fuss" vorher aufräumen.)
    		VariantClear(out_result);
    		return hr;
    	}
    
    	// Alles OK => Erfolg zurückmelden
    
    	return S_OK;
    }
    

    Falls du den eigentlichen COM Teil schon hast, sollte das alles sein was noch fehlt. Wenn der Code nicht funktioniert schreib nochmal (aber bitte mit genauer Fehlerbeschreibung).
    (Und wenn er funktioniert darfst du natürlich auch gerne nochmal schreiben 🤡 )



  • 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.


Anmelden zum Antworten