0

I created a wrapper for a c console application where I define a struct to pass the output to a c# windows form. The struct contains a char* variable that must be correctly interpreted in the c# code. I used the IntPtr type but I did not obtain the wanted result, only a number which I think is the possible memory address.

C Part:

struct struct_name{
int a;
char* s;
}

extern __declspec(dllexport) struct_name compute_calc(int opt, char* file_1, char* file_2)

C# Part:

[DllImport("dll_name.dll", CallingConvention = CallingConvention.Cdecl)]
        public static extern struct_name compute_calc(int opt, String file1, String file2)

[StructLayout(LayoutKind.Sequential, Pack = 1, CharSet = CharSet.Auto)]    
public struct struct_name{
        Int32 a;
        IntPtr s;
        }

In the application I call the function with the following code

struct_name result = levenshtein(1, filePath1, filePath2);

My problem at this point is to use the char*/IntPtr to extract the string contained in the struct. I tried to use a marshalling operation as suggested in How can I convert an unmanaged IntPtr type to a c# string? but my result is only a number. Is there any other way to convert the IntPtr to the correct string used in the c code?

edit: the integer in the struct is correctly passed. The problem is only in the char*

edit2:

struct_name result;

{..some code...}--> output int N, char* s0


result.s = (char*)malloc( sizeof(char)*n);

result.a=N;
result.s=_strdup(s0)

return result;

This is the C part code as requested in a suggestion.

Community
  • 1
  • 1
user2893821
  • 73
  • 1
  • 8
  • Marshal.PtrToStringAnsi – Alex F Mar 18 '14 at 15:23
  • I'm not sure if this is the reason or not (been a while since I did interop coding), but why `char*` parameters you're passing as `string`, and yet `char*` struct member you declare as `IntPtr`? Have you tried declaring struct's s member as `string`? – LB2 Mar 18 '14 at 15:24
  • Add also `CharSet = CharSet.Ansi` to `compute_calc` PInvoke declaration. – Alex F Mar 18 '14 at 15:32
  • if I try to use a string type in the struct I have a System.Runtime.InteropServices.MarshalDirectiveException – user2893821 Mar 18 '14 at 15:34
  • What compiler are you using for the native code? – David Heffernan Mar 18 '14 at 15:52
  • the one included in visual studio express 2012 – user2893821 Mar 18 '14 at 15:55
  • Have you tried returning the char* directly? Or returning the struct as an out param. Also, why are you calling `malloc` only to leak the memory. `strdup` already allocates the memory. – David Heffernan Mar 18 '14 at 15:56
  • Yes, I tried to use an unsafe context with char*, but I obtained the same incomprehensible characters as said before. If I do not use the malloc call I obtain this error in the C# part: System.AccessViolationException – user2893821 Mar 18 '14 at 16:42
  • Why would an unsafe context help? The malloc is clearly wrong. Do you know what strdup does? In any case you clearly leak what came back from malloc. Perhaps you should give an sscce so we can give you a definitive answer. – David Heffernan Mar 18 '14 at 19:49
  • I'm sorry for the delay, but I can't post a code example. For the malloc problem without that part I have an AccessViolationException – user2893821 Mar 19 '14 at 08:01
  • Yes you can post code. You need to make an sscce. You have one or more faults in your code and we cannot identify them if you won't show code. – David Heffernan Mar 19 '14 at 08:54
  • As for the `malloc`, I cannot see why you insist that it is correct. Your code is akin to writing this: `i = 1; i = 2;`. You must appreciate that the first assignment is pointless. Your compiler ought to tell you that the value assigned to `result.s` can never be used. Do you enable compiler warnings? – David Heffernan Mar 19 '14 at 09:44
  • You are right for the malloc, I made a mistake in that case with strdup that allocates the memory itself, so I tried to use strncpy to copy n characters after the malloc call but I have the same AccessViolationException Do you know another way to save the char* s0 in my result.s? – user2893821 Mar 19 '14 at 11:17
  • @user your question was about interop and has been answered. You have other problems, bugs in your unmanaged code. It's not fair to expect us to debug them because they are unrelated to the question you asked, and because only you have the code. – David Heffernan Mar 23 '14 at 08:15
  • you're right, thank you for your help! I'll close the topic. – user2893821 Mar 24 '14 at 07:51

2 Answers2

3

To convert the IntPtr to a String use the Marshal.PtrToStringAnsi method

struct_name s = compute_calc(...);
string str = Marshal.PtrToStringAnsi(s.s);

Note that if the compute_calc function allocated memory it may need to be freed in managed code as well. Can't say for sure because it depends on implementation details of compute_calc

JaredPar
  • 733,204
  • 149
  • 1,241
  • 1,454
  • I tried the suggested solution but the output string is a series of incomprehensible characters like "ìììììýýýýýýýý=e>>Y*", for that reason I tried to use the PtrStringToAuto Method but in that case the output was a series of ??? – user2893821 Mar 18 '14 at 15:38
  • @user2893821 that code should work fine. How is the `compute_calc` method creating the string? Can you show the code? – JaredPar Mar 18 '14 at 15:39
  • struct_name result; {..some code...}--> output int N, char* s0 result.s = (char*)malloc( sizeof(char)*n); result.a=N; result.s=_strdup(s0) return result; In the C part the code is like this, everything is fine for the int value, but for the string I have the problem as described – user2893821 Mar 18 '14 at 15:47
  • @user2893821 what is the contents of s0 before the _strdup call? – JaredPar Mar 18 '14 at 15:49
  • @user2893821 what calling convention are you using when you compile the project? – JaredPar Mar 18 '14 at 15:58
  • the one used is CallingConvention = CallingConvention.Cdecl – user2893821 Mar 18 '14 at 16:38
  • @user Yes we can see that. Jared wonders about the C code. Although I think it might actually be C++ FWIW. – David Heffernan Mar 18 '14 at 16:58
3

Marshal.PtrToStringAnsi() is what you need to convert your IntPtr to a string. The IntPtr holds a char*, and Marshal.PtrToStringAnsi() is your guy.

However, you've already tried that without success. So I suspect that your problem is more fundamental. Perhaps you have a binary mismatch in the way the two sides of the interop boundary treat the large struct. This is one part of interop where different tools behave differently. Structs should always be returned using out parameters. Change the native code to be like this:

__declspec(dllexport) int compute_calc(int opt, const char* file_1, 
    const char* file_2, struct_name* result)

And the managed side to be:

[StructLayout(LayoutKind.Sequential)]    
public struct struct_name{
    int a;
    IntPtr s;
}

[DllImport("dll_name.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern int compute_calc(int opt, string file1, string file2,
    out struct_name result);

Note that I also removed the Pack = 1 from your struct declaration. Unless the C code uses #pragma pack, and it should not do so, then this would cause a mismatch under 64 bit code.

David Heffernan
  • 601,492
  • 42
  • 1,072
  • 1,490