2

My c++ function is given below

# define MyFunction _declspec(dllexport)
extern "C" {

MyFunction int SubtractNumbers(int a, int b)
{
    return a - b;
}

MyFunction const char* GetStringKey()
{
    return "My String";
}
}

Calling c++ function from windows form is given below,

    [DllImport(cppFunctionsDll, CallingConvention = CallingConvention.Cdecl)]

    private const string DllFilePath = @"D:\\Projects\\December\\17-12-2020\\project-device- 
    setup\\Debug\\AccurynCPP.dll";

    [DllImport(DllFilePath, CallingConvention = CallingConvention.Cdecl)]
    public static extern int SubtractNumbers(int a, int b);

    [DllImport(DllFilePath, CallingConvention = CallingConvention.Cdecl)]
    public static extern string GetStringKey();
    
    public void GetSubtract()
    {
        lblSubtract.Text= Convert.ToString(SubtractNumbers(1, 2));
        
    }
    public void GetStringFunction()
    {
        lblstringKey.Text= GetStringKey();
        
    }

From the above function, GetSubtract() worked perfectly. But GetStringKey() not worked. when it reach on it's function while debugging, it automatically cancelled running mode in visual studio. How can i fix this issue.

Noufal
  • 115
  • 10
  • Have you tried `CallingConvention = CallingConvention.Cdecl` for `GetStringKey`? You may find some information here: https://stackoverflow.com/questions/15660722/why-are-cdecl-calls-often-mismatched-in-the-standard-p-invoke-convention – SGKoishi Dec 18 '20 at 05:07
  • You don't specify the calling convention on the `extern C` side, but you specify two different ones on the .NET side. Everything needs to be exactly correct for this to work. You also don't specify how the returned string should be marshaled back to the caller. – Flydog57 Dec 18 '20 at 05:17
  • @Flydog57, can you please explain with example – Noufal Dec 18 '20 at 05:40
  • Long read: [Default Marshaling Behavior](https://learn.microsoft.com/en-us/dotnet/framework/interop/default-marshaling-behavior), [Default Marshaling for Strings](https://learn.microsoft.com/en-us/dotnet/framework/interop/default-marshaling-for-strings), [Blittable and Non-Blittable Types](https://learn.microsoft.com/en-us/dotnet/framework/interop/blittable-and-non-blittable-types) – Jimi Dec 18 '20 at 07:19

1 Answers1

0

The calling convention describes to the compiler and linker how parameters, the stack and registers are to be handled. So if these don't match things won't work or memory can be corrupted. For instance, if the caller thinks the first parameter should be passed in the register eax, but the callee thinks it's on the stack. Things will obviously not work. For more info on calling conventions Wikipedia has a good description here. The calling convention you choose isn't very important, unless the compiler forces one on you. For instance for 64 bit programs Microsoft only uses one to my knowledge. So, I suggest adding __cdecl in front of the functions you have written. And using CallingConvention.Cdecl on the DLLImport lines.

Also, all the data types must exactly match. For instance:

[DllImport(DllFilePath, CallingConvention = CallingConvention.Cdecl)]
public static extern string GetStringKey();

This says it is returning a string, however despite our using the term string to represent a series of characters followed by a null. In this case string is a formal type. Which is they type std::string. Which is not what is being returned by the C code. The C code is returning a pointer to a char.

Generally passing complex types based on a library is kinda iffy. That's because it requires both languages to use the exact data structure to represent the type. So when crossing language boundaries it's often useful to stay with your own custom structures and very primitive types.

So I recommend your C function be this instead:

__cdecl bool GetStringKey(char *buffer, int maximumLength)
{
    // Write code here to copy all the characters from your "My String"
    // to the buffer or use a standard library function for copying char * strings (often called C strings) when copying make sure to copy the null character at the end. It's definitely needed.
    return true; // Yes it fit
}

You will need to change your DllImport to match. Once that is working you can add code to never go over the maximum buffer length (to prevent memory corruption) and maybe change the bool to the length of the string or -1 for failure.

I suggested this particular function prototype because it doesn't pass memory between languages. Instead the C code fills in a buffer from the C++ code. Which is the safest way to do something. Passing memory between unrelated modules (in this case C and C++ doesn't always work). Though I suspect in this case your C and C++ compiler and libraries are enough alike. That you could just switch to returning a std::string. As long as both sides match perfectly.

  • Thank you for your response. how to give __cdecl in function? Where to apply __cdecl? i mean c++ function or .Net function? Can you give an example from my question – Noufal Dec 18 '20 at 06:01
  • I have tried with the code => MyFunction const char* __cdecl GetStringKey() in c++ code, but still getting same error – Noufal Dec 18 '20 at 06:19
  • I re-read your code and saw another issue, and added to my answer. – Jeff Spencer Dec 18 '20 at 10:38
  • __cdecl bool GetStringKey(char *buffer, int maximumLength) i have given the above function & getting error. can you help me for giving example – Noufal Dec 21 '20 at 05:46