Samstag, 20. Oktober 2007

Zweidimensionales Array in einer C++ COM-Komponente erzeugen und an VB/VBA zurückgeben

Ich habe den Code aus einer konkreten Anwendung herausgelöst und die Bezeichner verallgemeintert, das Ergebnis aber nicht getestet.
Womöglich habe ich den einen oder anderen Tippfehler oder ähnliches hinzugefügt ...

Ein Beispiel für SafeArrayGetElement() fehlt noch.
Bei Gelegenheit werde ich das nachzuholen.

   1 // MyCOM.idl
2 interface ICo4COM : IUnknown {
3 HRESULT GetArray([out,retval] SAFEARRAY(short)* arr);
4 }
5
6 // MyCOM.h
7 interface IMyCOM : public IUnknown {
8 virtual HRESULT __stdcall GetArray(SAFEARRAY(short)** arr) = 0;
9 }
10
11 // MyCom.cpp
12 class MyCOMImpl : public ICo4COM {
13 private;
14 SAFEARRAY* arr;
15 SAFEARRAYBOUND rgsabound[2];
16
17 ~Co4COMImpl() {
18 gObjCnt--;
19
20 if(arr != NULL)
21 {
22 SafeArrayDestroy(arr);
23 arr = NULL;
24 }
25 }
26
27 HRESULT __stdcall GetArray(SAFEARRAY** arr) {
28 *arr = this->arr;
29 return S_OK;
30 }
31
32 void AddElement(long x, long y, short val) {
33 long index[2];
34 index[0] = x;
35 index[1] = y;
36
37 short data = val;
38 SafeArrayPutElement(arr, index, &data);
39 }
40
41 long ArrayXCount() {
42 return long(rgsabound[0].cElements);
43 }
44
45 long ArrayYCount() {
46 return long(rgsabound[1].cElements);
47 }
48
49 bool InitArray(long sizeX, long sizeY) {
50 short data = 0;
51 long index[2];
52
53 rgsabound[0].lLbound = 0;
54 rgsabound[0].cElements = sizeX;
55 rgsabound[1].lLbound = 0;
56 rgsabound[1].cElements = sizeY;
57
58 if(arr != NULL)
59 SafeArrayDestroy(arr);
60
61 arr = SafeArrayCreate(VT_I2, 2, rgsabound); // array of short
62
63 if(arr == NULL) { //SafeArray create failed
64 return false;
65 }
66
67 // init with 0
68 for(long i = 0; i <>cols; i++) {
69 for(long j = 0; j <>rows; j++) {
70 index[0] = i;
71 index[1] = j;
72 data = 0;
73
74 SafeArrayPutElement(arr, index, &data);
75 }
76 }
77
78 return true;
79 }
80 }

Kommentare:

Zoechi hat gesagt…

Unter VB/VBA kann das zurückgegebene SAFEARRAY folgendermaßen genutzt werden:

Dim arr as Variant

arr = COMComponent.GetArray()

Debug.Print arr(3,5)

Zoechi hat gesagt…

Falls folgender Fehler beim Compilieren auftritt:

“Fatal error C1189: #error : You need a Windows 2000 or later to run this stub because it uses these features: /robust command line switch.”

gibt's genauere Informationen unter http://blogs.msdn.com/eldar/archive/2006/02/28/540981.aspx

Elmar hat gesagt…

Ausgabe SafeArray in VBA
_______________________________

Dim arr

arr = c4cObj.c4cGetGameArray
For i = LBound(arr) To UBound(arr)
For j = LBound(arr, 2) To UBound(arr, 2)
Me.Cells(OrigX + i + 1, OrigY + j + 1) = arr(i, j)
Next j
Next i

-----------------------------

anders hats bei mir nicht gefunkt..

Zoechi hat gesagt…

Das SAFEARRAY wir in VB/VBA scheinbar freigegeben sobald der Scope der Variable an die das SAFEARRAY zugewiesen wurde verlassen wird.

In der COM-Komponente muss das SAFEARRAY so wie es aussieht jedesmal neu erstellt werden, bevor es zurückgegeben wird.

Elmar hat gesagt…

bei mir war es genau das problem, dass das safearray im excel - client verloren ging - des rätsels lösung - eine kopie vor der rückgabe anlegen:

HRESULT __stdcall c4cGetGameArray(SAFEARRAY** arr) {
// we have to copy the safearray, otherwise it will be destroyed
// by the client
SAFEARRAY * returnArray;
SafeArrayCopy(this->gameMatrix, &returnArray);
*arr = returnArray;
return S_OK;
} // c4cGetGameArray