2016-02-04 8 views
10

Ich habe (Legacy) VB6-Code, den ich von C# -Code verbrauchen möchte.Consuming VB6 String-Array in C#

Dies ist etwas ähnlich wie this question, aber es bezieht sich auf die Übergabe eines Arrays von VB6 mit einer C# dll. Mein Problem ist das Gegenteil.

In VB gibt es eine Schnittstelle in einer DLL und eine Implementierung in einer anderen.

Schnittstelle:

[ 
    odl, 
    uuid(339D3BCB-A11F-4fba-B492-FEBDBC540D6F), 
    version(1.0), 
    dual, 
    nonextensible, 
    oleautomation, 
     helpstring("Extended Post Interface.")   
] 
interface IMyInterface : IDispatch { 

    [id(...),helpstring("String array of errors.")] 
    HRESULT GetErrors([out, retval] SAFEARRAY(BSTR)*); 
}; 

Implementation (fragment) in cMyImplementationClass:

Private Function IMyInterface_GetErrors() As String() 

    If mbCacheErrors Then 
     IMyInterface_GetErrors = msErrors 
    End If 

End Function 

I gewickelt diese 2 DLLs mit tlbimp.exe und versuchen, die Funktion von C# zu nennen.

public void UseFoo() 
{ 
    cMyImplementationClass foo; 
    ... 
    var result = foo.GetErrors(); 
    ... 
} 

foo.GetErrors Calling() bewirkt, dass ein SafeArrayRankMismatchException. Ich denke, das deutet auf ein Marshalling-Problem hin, wie es im Abschnitt "Sichere Arrays" beschrieben ist: here.

Die Empfehlung scheint zu sein, den Parameter/sysarray von tlbimp.exe zu verwenden oder die erzeugte IL manuell zu bearbeiten, die ich ausprobierte.

Das Original IL sieht wie folgt aus:

.method public hidebysig newslot virtual 
    instance string[] 
    marshal(safearray bstr) 
    GetErrors() runtime managed internalcall 
{ 
    .override [My.Interfaces]My.Interface.IMyInterface::GetErrors 
} // end of method cImplementationClass::GetErrors 

Während die aktualisierte Version ist:

.method public hidebysig newslot virtual 
    instance class [mscorlib]System.Array 
    marshal(safearray) 
    GetErrors() runtime managed internalcall 
{ 
    .override [My.Interfaces]My.Interface.IMyInterface::GetErrors 
} // end of method cImplementationClass::GetErrors 

I identische Funktionssignatur Änderungen sowohl in der Schnittstelle und Implementierung gemacht. Dieser Prozess wird beschrieben here. Es spezifiziert jedoch keinen Rückgabewert in der Funktion (es verwendet eine "in" -Referenz) und verwendet auch keine Schnittstelle. Wenn ich meinen Code ausführen und von C# aufrufen, erhalte ich den Fehler

Methode nicht gefunden: 'System.Array MyDll.cImplementationClass.GetErrors()'.

Es scheint zu sein, dass etwas in der IL falsch ist, dass ich bearbeitet, obwohl ich nicht weiß, wohin man von hier zu gehen.

Wie kann ich diese Funktion von C# verwenden, ohne den VB6-Code zu ändern?

--Edit-- Neudefinition von "msErrors", die das private Array initialisiert, das zurückgegeben wird.

ReDim Preserve msErrors(1 To mlErrorCount) 

Wenn ich richtig verstehe, die „1“ in das bedeutet, dass die Anordnung von 1 indiziert wird anstelle von 0, was die Ursache für die Ausnahme ist, bekomme ich geworfen zu sehen.

+1

Ich verstehe, dass Sie es arbeiten zuerst erhalten möchten, aber die Bearbeitung der IL scheint nicht wie eine langfristige Lösung. –

+0

Vielleicht, aber es ist die empfohlene Praxis für Marshalling Änderungen erwähnt [hier] (https://msdn.microsoft.com/en-us/library/ek1fb3c6 (v = vs.100) .aspx # cpconeditingmicrosoftintermediatelanguagemsilanchor4). FWIW, das/sysarray-Flag scheint denselben Nettoeffekt zu haben, einschließlich des resultierenden Fehlers. – ayers

+0

Sie haben nicht gezeigt, wie Sie das Array deklarieren, das Sie aus VB6-Code zurückgeben. Hat es Rang 1 und untere Grenze 0, d. H. Als etwas wie "Dim msErrors (0 bis N) As String" deklariert? Wenn mbCacheErrors false ist, scheint Ihre aktuelle Implementierung ein nicht initialisiertes Array zurückzugeben. – Joe

Antwort

1

Ich habe alle Ihre Schritte gefolgt, außer TlbImp.exe. Stattdessen habe ich die DLLs direkt in die C# -Projektverweis hinzugefügt. Dadurch erhalte ich IL, die eine Kreuzung zwischen beiden Proben ist zu folgendem Ergebnis:

.method public hidebysig newslot abstract virtual 
     instance class [mscorlib]System.Array 
     marshal(safearray bstr) 
     GetErrors() runtime managed internalcall 
{ 
    .custom instance void [mscorlib]System.Runtime.InteropServices.DispIdAttribute::.ctor(int32) = (01 00 00 00 03 60 00 00)       // .....`.. 
} // end of method _IMyInterface::GetErrors 

ich den gleichen Code wie Sie getan haben, und im Wesentlichen auf eine Variable vom Typ Array Sie zuweisen.Während die CLR Arrays mit anderen Untergrenzen als 0, AFAIK, unterstützt, unterstützt keine Sprache, auch nicht VB.NET, sie in der Sprache.

Mein Testcode wird:

cMyImplementationClass myImpClass = new cMyImplementationClass(); 
IMyInterface myInterface = myImpClass as IMyInterface; 

myImpClass.CacheErrors = true; 

// Retrieve the error strings into the Array variable. 
Array test = myInterface.GetErrors(); 

// You can access elements using the GetValue() method, which honours the array's original bounds. 
MessageBox.Show(test.GetValue(1) as string); 

// Alternatively, if you want to treat this like a standard 1D C# array, you will first have to copy this into a string[]. 
string[] testCopy = new string[test.GetLength(0)]; 
test.CopyTo(testCopy, 0); 
MessageBox.Show(testCopy[0]); 
+0

Das war definitiv die richtige Antwort. Diese IL ist genau das, was von TlbImp.exe mit dem Flag/sysArray erzeugt wird. Ich denke jedoch, dass meine Probleme durch einige Dinge noch verschärft wurden. Für jeden, der ähnliche Probleme hat, scheint es, dass das Deklarieren des Ergebnisses als 'Array' (statt' var') wichtig war. Ich musste auch die ursprünglichen Baugruppen aus dem GAC löschen und sie durch die neuen ersetzen. Es scheint, dass meine ursprüngliche Implementierung zum Zeitpunkt der Kompilierung korrekt war, aber IIS verwendete zur Laufzeit veraltete Assemblys im GAC. – ayers