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#2331792BSTR* dataPtr
muss dann zuVARIANT* 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
stattVT_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.