65.9K
CodeProject is changing. Read more.
Home

Call C# code from C++ and read an array of struct which contains strings

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.05/5 (12 votes)

Nov 2, 2006

CPOL

2 min read

viewsIcon

136672

downloadIcon

5273

How to call C# code from C++ and read an array of struct which contains strings.

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.

  1. Open the C# solution in a VS 2005.
  2. Build the solution.
  3. Use regasm to register the tlb.
  4. Open the C++ dsw in VS 6.
  5. In the #import section, refer to the appropriate location in your machine. "#import "E:\MyTestApps\TestInterOp\Debug\MyInterOp.tlb"".
  6. Run the Console application and that's it.