2016-02-04 26 views
10

私はC#コードから消耗したい(レガシー)VB6コードを持っています。VB6の文字列配列をC#で使用する

これはthis questionと多少似ていますが、VB6からC#dllを使用する配列を渡すことを指します。私の問題は反対です。

VBでは、あるDLLにはインターフェイスがあり、別のDLLにはインターフェイスがあります。

インタフェース:

[ 
    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)*); 
}; 

cMyImplementationClassで実施(フラグメント):

Private Function IMyInterface_GetErrors() As String() 

    If mbCacheErrors Then 
     IMyInterface_GetErrors = msErrors 
    End If 

End Function 

私はTlbimp.exeをとC#から機能を呼び出すための試みでこれら2つのDLLを包みました。

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

foo.GetErrors()を呼び出すと、SafeArrayRankMismatchExceptionが発生します。私はこれがSafe Arraysセクションhereに記載されているマーシャリングの問題を示していると思います。

tlbimp.exeの/ sysarrayパラメータを使用するか、手動で作成したILを手動で編集することをお勧めします。

原ILは、次のようになります

.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 

更新されたバージョンであるが:

.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はインタフェースおよび実装の両方に同一の機能署名変更を行いました。このプロセスはhereと記載されています。ただし、関数の戻り値は指定されず( "in"参照が使用されます)、インタフェースも使用されません。コードを実行してC#から呼び出すと、エラーが発生します。

メソッドが見つかりませんでした: 'System.Array MyDll.cImplementationClass.GetErrors()'。

ここからどこに行くのかはわからないが、私が編集したILに何か問題があるようです。

VB6コードを変更せずにC#からこの機能をどのように使用できますか?

--Edit-- "msErrors"の再定義。返されるプライベート配列を初期化します。

ReDim Preserve msErrors(1 To mlErrorCount) 

私が正しく理解していれば、その中の「1」は、配列が、私はスロー見る例外の原因である、1ではなく0からインデックスされることを意味しています。

+1

私はあなたはそれが最初に作業していて、ILは、長期的な解決策のように見えるしていません編集を取得したいことを理解しています。 –

+0

多分そうですが、[ここ](https://msdn.microsoft.com/en-us/library/ek1fb3c6(v = vs.100).aspx#cpconeditingmicrosoftintermediatelanguagemsilanchor4)に記載されている変更をマーシャリングすることをお勧めします。 FWIWでは、/ sysarrayフラグは結果として生じるエラーを含む同じネットエフェクトを持つように見えます。 – ayers

+0

VB6コードから返す配列をどのように宣言しているかわかりません。ランク1と下限0を持っていますか?「Dim msErrors(0 To N)As String」のように宣言されていますか?また、mbCacheErrorsがfalseの場合、現在の実装では初期化されていない配列が返されているようです。 – Joe

答えて

1

TlbImp.exe以外のすべての手順を実行しました。代わりに、C#プロジェクト参照に直接DLLを追加しました。これをやって、私はあなたが与えるサンプルの両方の間のクロスですIL得る:私はあなたと同じコードを行っている

.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 

を、そして基本的にあなたはタイプArrayの変数に代入されています。CLRは0以外の境界を持つ配列をサポートしていますが、AFAIKでは言語がなく、VB.NETであっても、言語の本質的にそれをサポートします。

私のテストコードは次のようになります。

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

これは間違いなく正しい答えでした。このILは、/ sysArrayフラグを使用してTlbImp.exeによって生成されたものですが、私の問題はいくつかのことで複合化されている可能性があります。 似たような問題を抱えている人は、 'var'の代わりに' Array'として結果を宣言することが重要だったようです。 元のアセンブリをGACから消去し、新しいアセンブリに置き換える必要もありました。私の元の実装はコンパイル時に正しいと思われますが、実行時のIISはGACで古いアセンブリを使用していました。 – ayers

関連する問題