将字符串从C#发送到C ++ dll文件

时间:2018-03-08 13:26:15

标签: c# c++ string

我在socket.dll库中有一个函数,如下所示:

 __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));

 }

当我使用“dumpbin / exports socket.dll”检查socket.dll签名的GetPib函数时,它返回

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) 

我在C#项目(WindowsFormsApp1)中使用相同的签名来调用/调用GetPib函数。

以下是调用GetPib函数的C#源代码:

 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);

         }
 }

当我调用GetPib函数(如GetPib(0x1004xxx4))时,它会调用socket.dll的GetPib函数,但该值与特殊字符的值不同

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

如何解决此问题。

2 个答案:

答案 0 :(得分:0)

首先,在extern "C"中包装非托管函数声明以消除修改并将非托管函数重写为更适合与托管代码互操作的内容。

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;
  }

}

在这次重写中,我们传递托管字符串,一个分配的缓冲区来保存结果,以及该缓冲区的长度。此版本处理char*个字符串。如果你需要使用wchar_t*个宽字符串,转换它应该是微不足道的。需要注意的主要是我们没有使用任何C ++类型作为函数的参数,因为interop marshaller无法知道如何处理这些类型。我们只使用语言原语。

托管P / Invoke签名如下所示:

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

要打电话:

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);
}

这里我们分配非托管内存,以​​便在我们与非托管代码进行互操作时GC不会移动它。非托管函数将使用生成的char*填充缓冲区,我们将其转换回PtrToStringAuto()的托管字符串。然后我们释放非托管内存。

答案 1 :(得分:0)

我按照这个链接

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

我在C ++中修改了GetPib api

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

 extern  BSTR __declspec(dllexport)  GetPib(BSTR pib_key_chars)

来自

的修改过的C#文件
[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);

 [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);

解决了我的问题!!!!!!