1

I am trying to call a C# lib from a C++ program. C# and this interop is pretty new for me. Some of the communications have been easy until I had to face strings inside structures. I have been reading a lot and saw several examples, but I am unable to replicate them and make them work.

I extracted the code with the example of sending a string, and retriving the string which works (_string_exchange). And the method _return_struct which returns a struct with a string that doesnt works. Debugger fails when I try to use the variable for std::cout, with an unhandled exception from 0x00007FFFEA98A388 (KernelBase.dll). The Console.WriteLine havent wrote anything during the call.

I assume this is a problem matching the scrutures. Both are compiled in release x64, using .NET Framework 4.6.1. Also I have been checking with sizeof() and Marshal.SizeOf() to check that both have the same byte length. Also tried to change c++ project character from unicode to multibyte without success.

I saw examples like this that were pretty good explaning everything, but I dont know what I am missing: Passing strings/arrays within structures between C++/C#

C++ program:

struct myStruct
{
    int myInt;
    double myDouble;
    bool myBool;
    char myString[64];
}; 

int main() {
    const TCHAR* pemodule = _T("F:\\PATH\\TO\\DLLsi\\LibCSharp.dll");
    HMODULE lib = LoadLibrary(pemodule);

    typedef LPCSTR(_cdecl *_string_exchange)(LPCSTR s);
    auto pString_exchange = (_string_exchange)GetProcAddress(lib, "_string_exchange");
    LPCSTR test = pString_exchange("LPCSTR test works fine");
    std::cout << test << std::endl;

    typedef myStruct(_cdecl *_return_struct)();
    auto pReturn_struct = (_return_struct)GetProcAddress(lib, "_return_struct");
    myStruct aStruct = pReturn_struct();
    std::cout << aStruct.myString << aStruct.myBool << " " << aStruct.myDouble << " " << aStruct.myInt << std::endl;

    return 0;
}

C# library:

namespace LibCSharp
{    
    public class Class1
    {
        [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
        public struct myStruct
        {
            public int myInt;
            public double myDouble;
            [MarshalAs(UnmanagedType.U1)]
            public byte myBool;
            [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 64)]
            public string myString;
        }

        [DllExport]
        public static myStruct _return_struct()
        {
            Console.WriteLine("test");
            myStruct a;
            a.myInt = 3;
            a.myDouble = 2;
            a.myBool = 1;
            Console.WriteLine(a.myBool);
            a.myString = "Hello world! My values are: ";//28
            Console.WriteLine(a.myString);
            return a;
        }

        [DllExport]
        public static string _string_exchange(string s)
        {
            Console.WriteLine(s);
            return s;
        }
    }
}

I am also aiming in a future to make this structure an array, I hope once this is solved I wont face much problems, but any comment in advance is also wellcome.

Thank you in advance

  • Can't you use c++-cli? I believe that would make this a lot easier. – πάντα ῥεῖ May 04 '19 at 11:50
  • True words. The problem with that nuget package is that it very rapidly turns into rocket science when you try to pass non-trivial data. The function pointer declaration is not correct, the default is `__stdcall`. Returning a structure as a function return value requires it to be "blittable", an expensive word that means that the managed layout must be the same as the native layout. Both the bool and the string make it non-blittable, replace with byte and a fixed-size buffer. Returning strings from a C or C++ function is always risky, hard to release the memory correctly. BSTR is better. – Hans Passant May 04 '19 at 12:13
  • I pretendt to avoid c++/cli and stay on pure c++, it gave me more headshakes than things where it helped so far. I could make a wrapper lib in c++/cli to do so, but I dont really like this solution. – Vash Sheppard May 04 '19 at 13:23
  • About this nuget package, you are right, it scalated quickly. I previously also tried with _stdcall but without success. The bool is already bittable changing it as byte in the C# part, if I comment the string in the structure, it works without problems. About the string, that's why i am trying to make it a fixed size buffer, even tried bstr, but not much success.... With the BSTR type, for realeasing memory correctly, you mean in C#? – Vash Sheppard May 04 '19 at 13:26

2 Answers2

0

Maybe you should try using Named Pipes to communicate between two processes. It allows you to open stream and transfer any byte arrays. So you can serialize any object to array and transfer it.

Here's working solution for C++ and C# communication.

Pipes are more flexible and system-independent than interop. But probably less efficient...

Coestaris
  • 23
  • 3
0

At first glance, it appears your c# code is marshaling that return string as a tchar[64] which is almost certainly a unicode wchar[64]; while your C++ code is expecting an ascii char[64] to be there.

Try changing the c++ definition to

struct myStruct
{
    int myInt;
    double myDouble;
    bool myBool;
    TCHAR myString[64];
}; 
Tim
  • 5,940
  • 1
  • 12
  • 18
  • I have been trying these types, but without any success :( – Vash Sheppard May 04 '19 at 13:22
  • It could also be the packing is different between your platforms. Try setting the packing size of your structure to make it match your C++ struct. Add `Pack=1` to your `[StructLayout]` attribute to get a 1 byte packing, or whatever packing your C++ code is using – Tim May 04 '19 at 13:40
  • already tried this without success, read about these and have tried all from 0 to 128 as dessesperate measure – Vash Sheppard May 04 '19 at 13:52