Introduction
My C++ code was trying to read an array of struct which contains string data in C#. To my surprise, I found that a struct array which contains only integers is very easy to get, but if you have a string in the array, you need to do some more things. I searched on Google, and a lot of people were talking about the same problem and getting an exception which says, "Old format or invalid type library". But no where was I able to find the answer, and since I was able to solve the problem myself, I am publishing the solution here.
Basic Ideas
I have a C# DLL which contains a struct; let's say:
public struct MyStruct
{
public string name;
public string surname;
public int age;
}
I have an interface which looks like this:
public interface ITest
{
MyStruct[] GetData
{
get;
}
}
And my main class is:
public class Test : ITest
{
MyStruct[] st = new MyStruct[2];
public Test()
{
st[0].name = "abc";
st[0].surname = "def";
st[0].age = 10;
st[1].name = "qwe";
st[1].surname = "rty";
st[1].age = 20;
}
public MyStruct[] GetData
{
get
{
return st;
}
}
}
Now build the DLL. Then, from the Visual Studio ommand prompt, type type "regasm MyInterOp.dll /tlb:MyInterOp.tlb". Have look at the tlb using OleViewer. Find the tag MyStruct
. It will have an LPSTR
.
Now create a console application in C++ like this:
HRESULT hr = CoInitialize(NULL);
ITest* pTest = NULL;
hr = CoCreateInstance(__uuidof(Test),NULL,
CLSCTX_INPROC_SERVER,__uuidof(ITest),(void**)&pTest);
MyInterOp::MyStruct HUGEP *pBSTR;
hr = SafeArrayAccessData(pTest->GetData, (void HUGEP* FAR*)&pBSTR);
printf("Name: %S \n",pBSTR[0].name);
printf("SurName: %S \n",pBSTR[0].surname);
printf("Age: %d \n",pBSTR[0].age);
printf("Name: %S \n",pBSTR[1].name);
printf("SurName: %S \n",pBSTR[1].surname);
printf("Age: %d \n",pBSTR[1].age);
When you run this application, it will give you an exception. If you debug it, you can see that the HRESULT is "-2147319783" which means "Old format or invalid type library." So LPSTR
is not going to work for us.
Solution
How can we solve this issue? Make your struct look like this:
[StructLayout(LayoutKind.Sequential)]
public struct MyStruct
{
[MarshalAs(UnmanagedType.BStr)]
public string name;
[MarshalAs(UnmanagedType.BStr)]
public string surname;
public int age;
}
Register the DLL once again and look at the tlb. See that now it is BSTR
instead of LPSTR
.
Now run the C++ Console application.
Requirements
In order to run the mz test application.
- Open the C# solution in a VS 2005.
- Build the solution.
- Use regasm to register the tlb.
- Open the C++ dsw in VS 6.
- In the
#import
section, refer to the appropriate location in your machine. "#import "E:\MyTestApps\TestInterOp\Debug\MyInterOp.tlb"
".
- Run the Console application and that's it.