2

I'm trying to import my C++ Dll in C#. It seems to work fine for functions without parameters but i'm having issues with my function which has some.

My C++ function :

__declspec(dllexport) bool SetValue(const std::string& strV, bool bUpload)
{ 
    return ::MyClass::SetValue(strV.c_str(), bUpload);              
}

It is wrapped in "extern "C" {"

The function calls another function which is :

bool SetValue(const char* szValue, bool bUpload)
{
}

My C# Function :

[DllImport("MyDll.dll", EntryPoint = "SetValue", CharSet = CharSet.Auto, SetLastError = true, CallingConvention = CallingConvention.Cdecl)]
        public static extern void SetValue([MarshalAs(UnmanagedType.LPStr)]string strVal, bool bUpload);

When I use the debug mode and enter in SetValue(const char* sZvalue, bool bUpload) function, the sZvalue is "0x4552494F" but when I try to expand Visual Studio 's view to see the value it says "undefined value".

Maybe somebody has an idea of what's wrong with my code ?

Thanks !

Scott Chamberlain
  • 124,994
  • 33
  • 282
  • 431
user2088807
  • 1,378
  • 2
  • 25
  • 47
  • 1
    C# has no idea what a `std::string` is. You need to export the version of the function that takes a raw character pointer. – Zac Howland Dec 04 '13 at 13:48

2 Answers2

5

You cannot hope to pass a std::string using pinvoke. A std::string is a C++ class that can only be used from C++ code.

Your options:

  1. Write a C++/CLI wrapper.
  2. Use interop friendly types such as const char* or BSTR.

You already appear to have at hand a version of the function that accepts const char*. You can p/invoke that easily enough.

[DllImport("MyDll.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern void SetValue(
    string strVal, 
    [MarshalAs(UnmanagedType.I1)]
    bool bUpload
);

Obviously you'd need to export the version of SetValue that accepted const char*.

Note that you should not use SetLastError here unless your API does actually call SetLastError. It would be unusual if it did. It tends to be Win32 API functions that do that.

And as @Will points out, you should use MarshalAs to tell the marshaller that the bool parameter is to be marshaled as a single byte C++ bool rather than the default 4 byte Windows BOOL.

David Heffernan
  • 601,492
  • 42
  • 1,072
  • 1,490
  • Doesn't C++ bool also need annotation when marshalling from C# bool? – Will Dean Dec 04 '13 at 14:37
  • Thank you ! I've replaced my std::string with a const char * and my C# look like yours. However when I try to read the value in the debug mode it is "V" instead of "VALUE", only the first letter. – user2088807 Dec 04 '13 at 16:47
  • Sounds like you are sending UTF16. What OS are you using? – David Heffernan Dec 04 '13 at 17:16
  • Anyway, add a MarshalAs to be explicit, or set CharSet in the DllImport attribute – David Heffernan Dec 04 '13 at 18:57
  • I'm on W7 x64. The charset is on "Auto" now. What MarshalAs I have to use ? – user2088807 Dec 04 '13 at 19:30
  • I've just tried "ChartSet.Ansi" and it seems to work in this case. Is this the right way to make it work in any case ? :) – user2088807 Dec 04 '13 at 19:34
  • Ah, `CharSet.Ansi`. Well that explains it. I took you at your word when you said *my C# look like yours*. Note that `CharSet` is not present in my code. Without it you get the default, which on the Windows desktop platform is `Ansi`. You can remove the `CharSet` setting altogether. Or specify `CharSet.Ansi`. Either way. Doesn't hurt to be explicit. – David Heffernan Dec 04 '13 at 19:38
  • So, are you still stuck? – David Heffernan Dec 05 '13 at 07:39
0

I am not sure but you should use StringBuilder instead try this:

[DllImport("MyDll.dll", EntryPoint = "SetValue", CharSet = CharSet.Auto, SetLastError = true, CallingConvention = CallingConvention.Cdecl)]
    public static extern void SetValue(StringBuilder strVal, bool bUpload);
Mahmoud Fayez
  • 3,398
  • 2
  • 19
  • 36