2016-02-04 18 views
10

Tôi có mã VB6 (cũ) mà tôi muốn sử dụng từ mã C#.Tiêu thụ mảng chuỗi VB6 trong C#

Điều này hơi giống với this question, nhưng nó đề cập đến việc truyền một mảng từ VB6 tiêu thụ một đĩa C#. Vấn đề của tôi là ngược lại.

Trong VB, có một giao diện trong một dll và triển khai trong một dll khác.

Interface:

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

thực hiện (fragment) trong cMyImplementationClass:

Private Function IMyInterface_GetErrors() As String() 

    If mbCacheErrors Then 
     IMyInterface_GetErrors = msErrors 
    End If 

End Function 

Tôi bọc những 2 dlls với Tlbimp.exe và cố gắng để gọi hàm từ C#.

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

Gọi foo.GetErrors() gây ra SafeArrayRankMismatchException. Tôi nghĩ rằng điều này cho thấy vấn đề marshaling như được mô tả trong phần Mảng an toàn here.

Đề xuất có vẻ là sử dụng tham số/sysarray của tlbimp.exe hoặc để chỉnh sửa thủ công IL được tạo, mà tôi đã thử.

Các IL gốc trông như thế này:

.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 

Trong khi phiên bản cập nhật là:

.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 

tôi thực hiện thay đổi chức năng chữ ký giống hệt nhau trong cả giao diện và thực hiện. Quá trình này được mô tả here. Tuy nhiên, nó không chỉ định giá trị trả về trong hàm (nó sử dụng tham chiếu "trong") và cũng không sử dụng giao diện. Khi tôi chạy mã và gọi từ C#, tôi gặp lỗi

Không tìm thấy phương thức: 'System.Array MyDll.cImplementationClass.GetErrors()'.

Dường như có sự cố trong IL mà tôi đã chỉnh sửa, mặc dù tôi không biết phải đi đâu từ đây.

Làm cách nào để có thể sử dụng hàm này từ C# mà không thay đổi mã VB6?

--Chỉnh sửa-- Định nghĩa lại "msErrors", khởi tạo mảng riêng được trả về.

ReDim Preserve msErrors(1 To mlErrorCount) 

Nếu tôi hiểu chính xác, "1" trong đó có nghĩa là mảng được lập chỉ mục từ 1 thay vì 0, là nguyên nhân của ngoại lệ tôi thấy bị ném.

+1

Tôi hiểu rằng bạn muốn nhận nó làm việc đầu tiên, nhưng việc chỉnh sửa các IL không có vẻ như một giải pháp lâu dài. –

+0

Có lẽ như vậy, nhưng đó là thực hành được khuyến nghị cho các thay đổi marshaling được đề cập [ở đây] (https://msdn.microsoft.com/en-us/library/ek1fb3c6 (v = vs.100) .aspx # cpconeditingmicrosoftintermediatelanguagemsilanchor4). FWIW, cờ/sysarray dường như có cùng hiệu ứng ròng, bao gồm cả lỗi kết quả. – ayers

+0

Bạn chưa chỉ ra cách bạn khai báo mảng mà bạn trả lại từ mã VB6. Liệu nó có xếp hạng 1 và thấp hơn bị ràng buộc 0, tức là tuyên bố là một cái gì đó giống như 'Dim msErrors (0 To N) là String'? Ngoài ra, nếu mbCacheErrors là sai, thực hiện hiện tại của bạn dường như đang trả về một mảng chưa được khởi tạo. – Joe

Trả lời

1

Tôi đã làm theo tất cả các bước của bạn, ngoại trừ việc sử dụng TlbImp.exe. Thay vào đó, tôi trực tiếp thêm các DLL vào tham chiếu dự án C#. Việc làm này, tôi nhận được IL đó là sự pha trộn giữa cả hai mẫu bạn đưa ra:

.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 

Tôi đã làm mã tương tự như bạn, và về cơ bản bạn đang gán cho một biến kiểu Array.Trong khi CLR hỗ trợ các mảng có giới hạn thấp hơn 0, AFAIK, không có ngôn ngữ, ngay cả VB.NET, hỗ trợ nó bằng công cụ trong ngôn ngữ.

mã kiểm tra của tôi trở thành:

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

Đây chắc chắn là câu trả lời đúng. IL này là chính xác những gì được sản xuất bởi TlbImp.exe bằng cách sử dụng lá cờ/sysArray, tuy nhiên, tôi nghĩ rằng vấn đề của tôi có thể đã được pha trộn bởi một vài điều. Đối với bất kỳ ai có vấn đề tương tự, có vẻ như tuyên bố kết quả dưới dạng 'Mảng' (thay vì' var') là quan trọng. Tôi cũng cần xóa các hội đồng gốc từ GAC và thay thế chúng bằng các bản mới. Dường như việc triển khai ban đầu của tôi có thể đúng vào thời gian biên dịch, nhưng IIS tại thời gian chạy đã sử dụng các tập hợp cũ trong GAC. – ayers