4

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 a c# 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)); 
2
  • 1
    What message (text) do you get with the MarshalDirectiveException? Commented Nov 6, 2013 at 14:54
  • The message says: Method's type signature is not PInvoke compatible. Commented Nov 6, 2013 at 15:15

1 Answer 1

2

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.

Sign up to request clarification or add additional context in comments.

12 Comments

i think i will do as you suggested, in this way i don't need to change the signature of the native code
Out of interest, who deallocates the strings?
Up to now i cannot give you the right answer because i really don't know. I am at the early stage of creating a wrapper from the NDB API provided by Oracle (MYSQL): the library is written in C++, i need to make a double wrapper from C++ to C and after from C to C#. So for this strings and surely for other furter things i need to take care of the deallocating
If the target code is C++ then single C++/CLI wrapper sounds best
Unfortunately I need 2 wrapper, one wrapper for pure C code/compiler and one for C# so in this way i should take 2 birds with one stone...i hope
|

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.