2

I am writing a C# code, and there is a code that needs calling an unmanaged C++ library.

The signature in the library's header is like this

bool GetValueFromFile(char* sPathToFile, char* &sResult);

What signature should I translate this in C#? I tried:

bool GetValueFromFile(string filePath, ref string result)

But it does not work. There is no exception and the return value is true. But the string result stays null. It is the same for out string result or StringBuilder result.

I use Marshal.GetDelegateForFunctionPointer to get the function pointer as delegate.

Louis Rhys
  • 34,517
  • 56
  • 153
  • 221
  • Possible duplicate - http://stackoverflow.com/questions/13993396/passing-char-pointer-from-c-sharp-to-c – Sayse Jun 25 '13 at 07:47
  • @Sayse, I think the OP is asking about the refrence to pointer, which is not discussed in your linked question – omer schleifer Jun 25 '13 at 07:51
  • @omerschleifer - I think you are correct but I think the solution may be the same (cdecl) – Sayse Jun 25 '13 at 07:54
  • 1
    If the original function is 'char*&' this function will NOT populate your buffer - it will give you pointer to a buffer it has alreasdy allocated for you. So IntPtr is the the type of sResult, and then you'll need (sorry for not being specific but I cannot access documentation from here) some Marshal mumbo-jumbo. – Milosz Krajewski Jun 25 '13 at 08:18
  • @MiloszKrajewski is it like what Matthew Watson suggested in his answer? The problem is my sResult is unchanged from the initial pointer :( – Louis Rhys Jun 25 '13 at 08:24
  • @LouisRhys I've fixed an error in my answer below to do with using `out IntPtr` - please review it! – Matthew Watson Jun 25 '13 at 12:50

2 Answers2

4

You can handle a reference to a pointer pretty much like a pointer to a pointer, at least as far as P/Invoke is concerned.

I think you will probably need to use an IntPtr for the sResult parameter, along with either Marshal.PtrToStringAnsi() or Marshal.PtrToStringAuto(), but it's a bit difficult to say without knowing whether the C/C++ function allocates the string memory or not.

If it works, you will probably still need to free the memory (after getting the string) using Marshal.FreeCoTaskMem() or Marshal.FreeHGlobal(), but again this is impossible to know for sure without knowing what the C/C++ function does.

NOTE: If using an IntPtr to get an output value, you will need to use out result or ref result.

Matthew Watson
  • 104,400
  • 10
  • 158
  • 276
  • is it plain IntPtr, or ref IntPtr or out IntPtr for the sResult? – Louis Rhys Jun 25 '13 at 08:00
  • doesn't seem to work.. the IntPtr that I pass is unchanged from the initial value. – Louis Rhys Jun 25 '13 at 08:21
  • @LouisRhys Well is it an input or an output? Does the C/C++ function allocate the memory for the string, or is it expecting a non-null pointer? You can't write this without knowing (and telling us about) the exact requirements and effects of the function you're calling. Can you show the calling code? – Matthew Watson Jun 25 '13 at 08:26
  • It is written by a third party, so I don't knwo about whether the function allocate the memory or expect a non null pointer. But sResult is supposed to be an output. We pass a file path in `sPathToFile`, the library is supposed to do something in the file and send us the result in sResult. – Louis Rhys Jun 25 '13 at 08:30
  • @LouisRhys If you don't know what memory allocation the function is doing, how will you know how to free that memory? This is starting to sound a bit flakey... Anyway, an `IntPtr` is the lowest-level way you can pass pointers to and from C/C++. If it doesn't work with that (i.e. if the IntPtr value doesn't change) then nothing else will. `IntPtr` is marshalled as a pointer, and you can't go lower-level than that. – Matthew Watson Jun 25 '13 at 08:35
  • Oh also I just noticed that you're using `GetDelegateForFunctionPointer()` - why are you using that and not a normal P/Invoke declaration? – Matthew Watson Jun 25 '13 at 08:40
  • let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/32321/discussion-between-louis-rhys-and-matthew-watson) because stackoverflow complains – Louis Rhys Jun 25 '13 at 08:41
  • @Matthew No, it's not plain `IntPtr`. It needs to be `out IntPtr` or perhaps `ref IntPtr` depending on the semantics. – David Heffernan Jun 25 '13 at 12:38
  • @DavidHeffernan Ah thanks, in his case I expect it will be `out`. I'll amend the answer. (Was clearly suffering from brainfade earlier ;) – Matthew Watson Jun 25 '13 at 12:48
1

You'll need to pass a pointer by reference. Assuming that sResult is passed from native to managed, i.e. that it has out semantics, here's the signature:

bool GetValueFromFile(string filePath, out IntPtr result);

Once you've called this you will need to convert it to a string:

IntPtr resultPtr;
if (GetValueFromFile(filePath, out resultPtr))
    string result = Marshal.PtrToStringAnsi(resultPtr);

It's not clear who is responsible for freeing the memory that the native code allocates. Presumably that is documented somewhere and you already know how to handle that issue.

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