简体   繁体   中英

marshalling a struct with char* to C#

I have the following struct used in a C program

typedef enum
{
  ndberror_st_success = 0,
  ndberror_st_temporary = 1,
  ndberror_st_permanent = 2,
  ndberror_st_unknown = 3
} ndberror_status_enum;

typedef enum
{
 ndberror_cl_none = 0,  
 ndberror_cl_application = 1,  
 ndberror_cl_no_data_found = 2,
 ndberror_cl_constraint_violation = 3,
 ndberror_cl_schema_error = 4,
 ndberror_cl_user_defined = 5,
 ndberror_cl_insufficient_space = 6,
 ndberror_cl_temporary_resource = 7,
 ndberror_cl_node_recovery = 8,
 ndberror_cl_overload = 9,
 ndberror_cl_timeout_expired = 10,
 ndberror_cl_unknown_result = 11,
 ndberror_cl_internal_error = 12,
 ndberror_cl_function_not_implemented = 13,
 ndberror_cl_unknown_error_code = 14,
 ndberror_cl_node_shutdown = 15,
 ndberror_cl_configuration = 16,
 ndberror_cl_schema_object_already_exists = 17,
 ndberror_cl_internal_temporary = 18
} ndberror_classification_enum;


typedef struct {
  ndberror_status_enum status;
  ndberror_classification_enum classification;
  int code;
  int mysql_code;
  const char * message;
  char * details;
} ndberror_struct;

I need to marshal it in ac# program, the C# side declaration is:

    public enum ndberror_status
    {
        ndberror_st_success = 0,
        ndberror_st_temporary = 1,
        ndberror_st_permanent = 2,
        ndberror_st_unknown = 3,
    }

    public enum ndberror_classification
    {
        ndberror_cl_none = 0,
        ndberror_cl_application = 1,
        ndberror_cl_no_data_found = 2,
        ndberror_cl_constraint_violation = 3,
        ndberror_cl_schema_error = 4,
        ndberror_cl_user_defined = 5,
        ndberror_cl_insufficient_space = 6,
        ndberror_cl_temporary_resource = 7,
        ndberror_cl_node_recovery = 8,
        ndberror_cl_overload = 9,
        ndberror_cl_timeout_expired = 10,
        ndberror_cl_unknown_result = 11,
        ndberror_cl_internal_error = 12,
        ndberror_cl_function_not_implemented = 13,
        ndberror_cl_unknown_error_code = 14,
        ndberror_cl_node_shutdown = 15,
        ndberror_cl_configuration = 16,
        ndberror_cl_schema_object_already_exists = 17,
        ndberror_cl_internal_temporary = 18,
    }
[System.Runtime.InteropServices.StructLayoutAttribute(System.Runtime.InteropServices.LayoutKind.Sequential)]
    public struct ndberror_struct
    {      
        public ndberror_status status;
        public ndberror_classification classification;
        public int code;
        public int mysql_code;
       [System.Runtime.InteropServices.MarshalAsAttribute(System.Runtime.InteropServices.UnmanagedType.LPStr)]
        public string message;
        [System.Runtime.InteropServices.MarshalAsAttribute(System.Runtime.InteropServices.UnmanagedType.LPStr)]
        public string details;
    }

The C-Side signature of the method to be called is:

const ndberror_struct __stdcall Ndb_getNdbError(void* obj);

The C#-Side instead is:

        [System.Runtime.InteropServices.DllImportAttribute("Ndb_CWrapper.dll", EntryPoint = "Ndb_getNdbError", CallingConvention = System.Runtime.InteropServices.CallingConvention.StdCall,CharSet = CharSet.Ansi)]
        private static extern ndberror_struct Ndb_getNdbError(System.IntPtr obj);

When i try to invoke the method from the managed code a MarshalDirectiveException raises. I think the problem is somewhere in the string marshalling, but i'm not able to solve the problem. The native library is compiled for a x64 system multibyte charset code (with visual studio c++ 2012)

The alternative can be some strange operation inside the native code, where i cast a NdbError (defined in another header) to ndberror_struct , here the code:

Ndb* tmp=(Ndb*)obj;
ndberror_struct tmpRes=(ndberror_struct)tmp->getNdbError();

The NdbStruct returned by tmp->getNdbError() overload the casting operator (i suppose), this is the declaration:

operator ndberror_struct() const {
    ndberror_struct ndberror;
    ndberror.status = (ndberror_status_enum) status;
    ndberror.classification = (ndberror_classification_enum) classification;
    ndberror.code = code;
    ndberror.mysql_code = mysql_code;
    ndberror.message = message;
    ndberror.details = details;
    return ndberror;
}

Any help will be appreciated.

Edit: I tried to change a bit the method's signature in this way: C-Side:

void* __stdcall Ndb_getNdbError(void* obj)

C#-Side:

IntPtr Ndb_getNdbError(IntPtr obj)

the native method return &tmpRes and in the managed code i had marshalled manually the struct calling

var tmp=Ndb_getNdbError(raw);
var f = (ndberror_struct)Marshal.PtrToStructure(tmp, typeof(ndberror_struct));

The marshaller won't marshal those char* fields from the native code to the managed. You'll have to do that bit yourself.

One way to do that is to change the C# struct declaration as follows:

public struct ndberror_struct
{      
    public ndberror_status status;
    public ndberror_classification classification;
    public int code;
    public int mysql_code;
    public IntPtr message;
    public IntPtr details;
}

Leave your p/invoke declaration as it is:

[DllImport("Ndb_CWrapper.dll")]
private static extern ndberror_struct Ndb_getNdbError(IntPtr obj);

You'll be able to call the function now. In order to convert the two IntPtr fields into strings, call Marshal.PtrToStringAnsi .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM