0

I get a c project named Clib2CS with two files which are Clib2CS.c and Clib2CS.h.

Clib2CS.h is as following:

__declspec(dllexport) typedef struct BTreeNode {
     int value;
     struct BTreeNode* left;
     struct BTreeNode* right;
 }BTnode;

__declspec(dllexport) unsigned long ConnectSession(unsigned long   handle,
     unsigned char * publicKey,
     unsigned char   publicKeyLen);


__declspec(dllexport) void bulidTree(BTnode* root, int value);

Clib2CS.c is as following:

#include "Clib2CS.h"
#include <stdio.h>
#include <stdlib.h>
unsigned unsigned long ConnectSession(unsigned long   handle,
                               unsigned char * publicKey,
                               unsigned char   publicKeyLen)
{
         return 42;
}


void bulidTree(BTnode* root, int value) {
    if (root == NULL) {
       BTnode* node = (BTnode*)malloc(sizeof(BTnode));
       node->value = value;
    }
    if (value < root->value) bulidTree(root->left, value);
    else bulidTree(root->right, value);
 }

This c project generates a Clib2CS.dll which will be called in a c-sharp project. The c# project contains only one file named Program.cs.

Progarm.cs is as following :

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Runtime.InteropServices;

namespace ConsoleApplication1
{
    [StructLayout(LayoutKind.Sequential)]
    public class BTnode {
    public int value;
    [MarshalAs(UnmanagedType.LPStruct)]
    public BTnode left;
    [MarshalAs(UnmanagedType.LPStruct)]
    public BTnode right;
}

class Program
{
    [DllImport("Clib2CS.dll", CallingConvention = CallingConvention.Cdecl)]
    unsafe static extern UInt32 ConnectSession(UInt32 handle, char* publickey, char publicKeyLen);

    [DllImport("Clib2CS.dll", CharSet = CharSet.Auto)]
    unsafe static extern void bulidTree([In, Out, MarshalAs(UnmanagedType.LPStruct)] BTnode root, int value);


    public unsafe static UInt32 GetConnectSession(UInt32 handle, string publickey, char publicKeyLen) {
        // "Convert" string to char*
        char* pubKey;
        fixed (char* bptr = publickey) {
            pubKey = (char*)bptr;
        }
        return ConnectSession(handle, pubKey, publicKeyLen);
    }

    static void Main(string[] args)
    {

        UInt32 ret =  GetConnectSession((UInt32)1, "helloworld", 'c');
        Console.WriteLine("####################: {0}", ret);

        BTnode root = new BTnode();
        root.value = 666;
        Console.WriteLine("value of root is : {0}", root.value);

        int[] vec = { 4, 5, 6, 7, 8, 9 };
        foreach (int item in vec) {
            bulidTree(root, item);
        }

        Console.WriteLine("----------------------------------------------");
        for (; root != null; root = root.right) {
            Console.WriteLine("the tree node is: {0}", root.value);
        }
    }
}
}

Run it, and I get this error:

Unhandled Exception:System.TypeLoadException: Cannot marshal field 'left' of type 'ConsoleApplication1.BTnode': There is no marshaling support for this type.

so, how do we invoke c struct of DLL from c# gracefully?

shaw
  • 13
  • 6
  • From what I remember pointer types equivalence in C# is `IntPtr`, Have you tried with it? – mrogal.ski Jul 28 '17 at 09:30
  • @m.rogalski yeah, I try it, and it doesn't work. I get error as `The * or -> operator must be applied to a pointer`. – shaw Jul 31 '17 at 01:27
  • Equivalent of `unsigned char` in C# is `byte` so this `unsigned unsigned long ConnectSession(unsigned long handle, unsigned char * publicKey, unsigned char publicKeyLen)` in C# will be `UInt32 ConnectSession(UInt32 handle, IntPtr publicKey, byte publicKeyLen)` – mrogal.ski Jul 31 '17 at 09:17
  • Yeah, the `ConnectSession` works well, but it's not the problem. Actually, I am confused on how to call `BTreeNode` in c#. As question says, how to invoke c struct in c#. Thanks, m.rogalski. – shaw Aug 01 '17 at 02:39

1 Answers1

0

You're too close.
I suggest you to use this for string conversion.

String managedString = Marshal.PtrToStringAnsi((IntPtr) (char *) myUnmanagedString);

You have to know that char* in c# and in c are different a char in c is in 1 byte and in c# in 2 bytes so the pointer are not the same.

Your question is a little generale.

  1. The first way is to marshall like you did in your first attempt.

  2. The second way is to create a CLI C++ DLL use your library or your DLL code as you did in this C project and because it's C++ so it's easy to use with your C code and C# both because of its nature to handle managed and unmanaged code as result it will like a normal .net dll all you have to do is to add it like a reference so i suggest to take a look to this article for more information:

Hicham Bouchilkhi
  • 682
  • 10
  • 29