-1

I call a native dll from C# Mono and I cannot capture the exceptions with a try catch clause c# side. Apparently Mono does not support that, regardless of the flags being set (as according to other posts here about this problem). Mono will always just close down as soon as the exception leaves the native side.

The solution, I came up with, is to pass an [Out]IntPtr errorText with all my dll extern methods from c#. This is recieved native side as a char**. The native c++ function wrap everything in a try catch(const std::exception& ex) clause. If there is no exception, I set the errorText to nullptr but if there is an exception I set it to *errorText=ex.what()

When the native call returns, it either has a null pointer to error, meaning no exception or it is non null in which case I extract it as Marshal.PtrToStringAnsi(errorText)

This works and does not work. The exceptions get caught and the pointer is being set, but the marshal call returns null from the IntPtr. After some testing, I realize that if I set the native error text pointer to a constant like *errorText="a test" then it works as intended.

The issue seems to be that the native exception object goes out of scope when the native function returns and at this point the what() text becomes invalid, meaning it is invalid when I try to marshal the contents from the pointer to it.

One solution is to always myself throw const strings as exceptions like throw "something bad" and catch that, because those constant strings remain valid, and then catch other std::exceptions returning just a generic error string like "undefined std::exception".

That is obviously not perfect, though it can work. The question is why exactly I cannot get to the *what() after leaving the function. While the actual exception object may go out of scope, the message it was created with is generally itself a constant. If I do throw std::exception("something") then what should point to the constant "something" and should remain valid after the exception goes out of scope.

I have considered making a persistent char array in the dll and copy the what() into it for later retrieval, but I need to support multiple simultaneous accesses, which could potentially have multiple exceptions at the same time, leading t fighting over this buffer.

I am very interested if anyone have some insight in why the what() is not available after exception leaves scope or a good idea for solving this more elegantly than by throwing string exceptions.

Edit: One other solution would be to allocate the string for the error message managed side and pass a pointer to that for native side to put an error into. I just need to not allocate a new string every call... would much prefer to just get a pointer to the actual message in what()

JoeTaicoon
  • 1,383
  • 1
  • 12
  • 28
  • Probably relevant. (http://stackoverflow.com/questions/150544/can-you-catch-a-native-exception-in-c-sharp-code) – Cheers and hth. - Alf Aug 15 '16 at 09:19
  • Probably solution: (http://stackoverflow.com/questions/6850091/how-to-catch-unmanaged-c-exception-in-managed-c) – Cheers and hth. - Alf Aug 15 '16 at 09:20
  • That certainly seems related, but I cannot throw an exception up from my native code, as in the referenced post. That causes an instant termination, and I can also not allocate a garbage collected for the message from the native code... which is pure c++ with no ms additions. Game me an idea, though, so I will extend my post – JoeTaicoon Aug 15 '16 at 10:02
  • I am curious about why this was down voted. It seems like it can be a general problem without a simple solution...? – JoeTaicoon Aug 27 '16 at 09:58

1 Answers1

2

You could allocate a char-buffer (within the native DLL) for your "errorText"-Parameter and copy the text "ex.what()" to that buffer. In that case, the Memory will stay valid. But I think, you will have to release the char-buffer by your own after you read the string in C# to prevent a memory-leak.

Schnied
  • 36
  • 1
  • Thanks, but I already considered and rejected that, as per my question. – JoeTaicoon Aug 15 '16 at 09:58
  • I was a bit unfair to your answer, because you did not actually say the buffer should be shared among all calls. The solution I use now is to allocate the buffer per exception and pass the pointer back to managed. Managed is then tasked with calling a native delete on that pointer, after having read the text. Therefore I should obviously accept your answer. Sorry if I misread you initially. – JoeTaicoon Aug 27 '16 at 09:56