3

I would like to make my extern C++ function return a message when an exception occurs. Something like this:

extern "C" __declspec(dllexport) const char* __stdcall Calculate(double &result, double a, double b) 
{
    try
    {
        result = InternalCalculation(a, b);
    }
    catch(std::invalid_argument& e)
    {
        return e.what();
    }
    return "";
}


double InternalCalculation(double a, double b)
{
    if(a < b)
    {
        const char* err = "parameters error!";
        throw std::invalid_argument(err);
    }
    return sqrt(a - b);
}

On the other hand, I call the function from my C# program and I would like to show error in a MessageBox:

[DllImport(@"MyDll.dll", EntryPoint = "Calculate")]
private static extern IntPtr Calculate(out double result, double a, double b);

private void Calculate()
{
    IntPtr err;     
    double result = 0;
    err = Calculate(out result, out 2, out 3);
    var sErr = Marshal.PtrToStringAnsi(err);
    if (string.IsNullOrEmpty(sErr))
        MessageBox.Show(sErr);
    ...
}

Unfortunately, it doesn't work.. the MessageBox just shows random characters.

I'm surprised because if I replace:

return e.what();

by:

const char* err = "parameters error!";
return err;

Then "parameters error!" will be shown correctly in the messagebox of the C# code. Both err and e.what() are the same type (const char*), so what's wrong with e.what()??

Eugene Loy
  • 12,224
  • 8
  • 53
  • 79

3 Answers3

2

When you are using a string literal as a return value from a function, it will work properly. But same is not the case with local variable e. You need to create a dynamic char* to store the value of e.what() in that and return that value.

dvai
  • 1,953
  • 3
  • 13
  • 15
  • 1
    I wrotre the following but now the C# program shut down after calling the function : int length = strlen(e.what()); char* err = new char[length]; strncpy_s(err, length, e.what(), length); return err; – user2508767 Aug 10 '13 at 15:27
  • 1
    char* err = new char[length+1]; // err needs to be deallocated at some point! – Edward Clements Aug 10 '13 at 16:23
  • 1
    Lets say the error code is the string "abc". This is of length 3, but there is one more \0 char which you need to consider while allocating space for new pointer. – dvai Aug 10 '13 at 16:24
  • 1
    @EdwardClements is right, remember to deallocate the char array in the calling code. – dvai Aug 10 '13 at 16:26
  • ah thank you. It works! I just wonder if there is a more simple way to copy the string. The following is a bit boring: int length = strlen(e.what()) +1; char* err = new char[length]; strncpy_s(err, length, e.what(), length); return err; – user2508767 Aug 10 '13 at 16:42
1

Have your function return a BOOL instead, with the error message string as a parameter, like:

BOOL Calculate(double &result, double a, double b, char *pMsg, size_t nMsgSize)
{   try
    {   result = InternalCalculation(a, b);
    }
    catch(std::invalid_argument& e)
    {   strncpy_s(pMsg, nMsgSize, e.what(), _TRUNCATE);
        return FALSE;
    }
    return TRUE;
}

and invoke it like

StringBuilder sErr = new StringBuilder(256);
if (!Calculate(out result, 2, 3, sErr, 256))
    MessageBox.Show(sErr);
Edward Clements
  • 5,040
  • 2
  • 21
  • 27
  • it means that the API will not cause an exception if the length of the text is greater than the buffer, it will just truncate it, I find this a lot less intrusive -- see [MSDN Article](http://msdn.microsoft.com/en-us/library/ms175769(v=vs.80).aspx) – Edward Clements Aug 10 '13 at 19:11
0

It's because the exception string is pointing to a string which destroyed once the exception handler is done. You might need to return e.g. std::string and marshal that into a C# string.

Some programmer dude
  • 400,186
  • 35
  • 402
  • 621
  • I think an external c function can't return std::string. At least, when I try to call with DllImport a function that returns std::string, it says 'no such entry point'. – user2508767 Aug 10 '13 at 15:07