-1

I have a function in socket.dll library as below:

 __declspec(dllexport) string GetPib(const wchar_t* pib_key_chars)
 {
    wstring ws(pib_key_chars);
    std::string pib_key(ws.begin(), ws.end());
        cout << "In GetPib" << "  and pib_key in string=" << pib_key << endl;
    Pib pb;

    return std::to_string(pb.Get(phyFskTxPacket, 0));

 }

When i use "dumpbin /exports socket.dll" to check the GetPib function of socket.dll signature it returns

1    0 00011370 ?GetPib@@YA?AV?$basic_string@DU?$char_traits@D@std@@V?
$allocator@D@2@@std@@V12@@Z = @ILT+875(?GetPib@@YA?AV?$basic_string@DU?
$char_traits@D@std@@V?$allocator@D@2@@std@@V12@@Z) 

I am using the same signature in the C# project(WindowsFormsApp1) to call/invoke GetPib function.

Below is the C# source code to invoke GetPib function:

 namespace WindowsFormsApp1
 {
     public partial class Form1 : Form
     {
         [DllImport("socket.dll", EntryPoint = "?GetPib@@YA?AV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@PB_W@Z", CallingConvention = CallingConvention.Cdecl)]
         public static extern string GetPib([MarshalAs(UnmanagedType.LPStr)] string pib_key);        

         public Form1()
         {
             InitializeComponent();
         }

         private void GetPib_button_Click(object sender, EventArgs e)
         {
             String str = pib_id_tbox.Text;

             pib_uvalue_tbox.Text = GetPib(str);

         }
 }

When I invoke GetPib function like GetPib(0x1004xxx4), it invokes the socket.dll's GetPib function but the value is different with special characters

str=0x10040004  tr=268697604-----> in C# file
In GetPib  and pib_key in string=h䤰„------> in C++ .dll file

How to fix this issue.

Santosh Sahu
  • 2,134
  • 6
  • 27
  • 51
  • hmm maybe make a wrapper for c++ dll (i mean console app to execute c++ funcions) and execute them from c# app? – Agent_Orange Mar 08 '18 at 13:29
  • You cannot use `std::string` or other C++ classes like that via P/INVOKE. You either need to change the signature to be something that works or create a wrapper. [std::string in C#?](https://stackoverflow.com/questions/874551/stdstring-in-c) – crashmstr Mar 08 '18 at 13:33
  • @crashmstr: My code already follow the example that you provided. could you please tell what more code change to be done, with a new code snippet. – Santosh Sahu Mar 08 '18 at 13:49
  • You are using the wrong `UnmanagedType` value for a wide character string. [String Marshaling](https://learn.microsoft.com/en-us/dotnet/framework/interop/default-marshaling-for-strings) – Mark Benningfield Mar 08 '18 at 14:29
  • @MarkBenningfield: I used UnmanagedType.LPWStr, but still same error. Could you please help, which one will fit for the solution. – Santosh Sahu Mar 08 '18 at 14:37
  • Are you sure the native method works correctly? You are instantiating a `std::string` from a range of wide characters. Why do you expect it to _not_ be gibberish? – Mark Benningfield Mar 08 '18 at 14:44
  • @SantoshSahu No, I don't see that your code follows any example from the answers. Your code returns a `std::string`, which is incompatible with C# p/invoke (the linked question takes a `std::string` as an input parameter, but same problem). – crashmstr Mar 08 '18 at 14:44
  • @crashmstr the error that i reported is not from returning the string from c++ to c# but sending string from c# to c++. Could you please find any snippet to solve the issue. – Santosh Sahu Mar 08 '18 at 15:22
  • @MarkBenningfield. I am new to to those wchar and tchar for windows. Could you please give any suggestions to solve the issue. – Santosh Sahu Mar 08 '18 at 15:23
  • 1
    Possible duplicate of [How to convert wstring into string?](https://stackoverflow.com/questions/4804298/how-to-convert-wstring-into-string) (there you go, code to convert from wide string to string). Come back and look at my comments once you've moved on to having problems with the p/invoke. – crashmstr Mar 08 '18 at 15:24

2 Answers2

0

First of all, wrap the unmanaged function declaration in extern "C" to eliminate the mangling and rewrite the unmanaged function to something more sensible for interop with managed code.

extern "C" {

  __declspec(dllexport) int GetPib(const char* pib_key_chars, char *result, int len) {
    cout << "In GetPib" << "  and pib_key in string=" << pib_key_chars << endl;
    Pib pb;
    string packet = std:to_string(pb.Get(phyFskTxPacket, 0);
    int n = (int)packet.length;
    if (n < len) {
      packet.copy(result, n, 0);
      result[n] = '\0';
    }
    return n + 1;
  }

}

In this rewrite, we are passing the managed string, an allocated buffer to hold the result, and the length of that buffer. This version handles char* strings. If you need to use wchar_t* wide strings, it should be trivial to convert it. The main thing to note is that we are not using any C++ types as parameters to the function, since the interop marshaller has no way of knowing how to deal with those types. We are only using language primitives.

The managed P/Invoke signature would look like this:

[DllImport("socket.dll", EntryPoint="GetPib")]
public static extern int GetPib(([MarshalAs(UnmanagedType.LPStr)] string keyChars, IntPtr buffer, int len);

To call it:

private void GetPib_button_Click(object sender, EventArgs e)
{
  int needed = GetPib(pib_id_tbox.Text, IntPtr.Zero, 0);
  IntPtr p = Marshal.AllocHGlobal(needed);
  GetPib(pib_id_tbox.Text, p, needed);
  pib_uvalue_tbox.Text = Marshal.PtrToStringAuto(p);
  Marshal.FreeHGlobal(p);
}

Here we are allocating unmanaged memory so that the GC won't move it around while we are interoperating with unmanaged code. The unmanaged function will fill the buffer with the resulting char*, which we convert back into a managed string with PtrToStringAuto(). Then we free the unmanaged memory.

Mark Benningfield
  • 2,800
  • 9
  • 31
  • 31
0

I followed this link

https://answers.unity.com/questions/142150/passing-strings-to-and-from-a-c-dll.html

I modified GetPib api in C++

 __declspec(dllexport) string GetPib(const wchar_t* pib_key_chars)

to

 extern  BSTR __declspec(dllexport)  GetPib(BSTR pib_key_chars)

Modified C# file from

[DllImport("socket.dll", EntryPoint = "?GetPib@@YA?AV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@PB_W@Z", CallingConvention = CallingConvention.Cdecl)]
     public static extern string GetPib([MarshalAs(UnmanagedType.LPStr)] string pib_key);

to

 [DllImport("socket.dll", EntryPoint = "?GetPib@@YAPA_WPA_W@Z", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Unicode)]
    [return: MarshalAs(UnmanagedType.BStr)]
    public static extern string GetPib(string str);

and it solved my problem!!!!!!

Santosh Sahu
  • 2,134
  • 6
  • 27
  • 51