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

Posted

技术标签:

【中文标题】将字符串从 C# 发送到 C++ dll 文件【英文标题】:Sending string from C# to C++ dll file 【发布时间】:2018-03-08 13:26:14 【问题描述】:

我在 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(0x1004xxx4)之类的GetPib函数时,它会调用socket.dll的GetPib函数,但值因特殊字符而异

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

如何解决这个问题。

【问题讨论】:

hmm 可能为 c++ dll 做一个包装器(我的意思是控制台应用程序来执行 c++ 函数)并从 c# 应用程序执行它们? 您不能通过 P/INVOKE 使用 std::string 或其他类似的 C++ 类。您要么需要将签名更改为有效的东西,要么需要创建一个包装器。 std::string in C#? @crashmstr:我的代码已经遵循您提供的示例。你能告诉我还需要做哪些代码更改,使用新代码 sn-p。 您对宽字符串使用了错误的UnmanagedType 值。 String Marshaling How to convert wstring into string? 的可能重复项(你去吧,从宽字符串转换为字符串的代码)。一旦你遇到 p/invoke 的问题,回来看看我的 cmets。 【参考方案1】:

首先,将非托管函数声明包装在 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++ 类型作为函数的参数,因为互操作编组器无法知道如何处理这些类型。我们只使用语言原语。

托管的 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() 将其转换回托管字符串。然后我们释放非托管内存。

【讨论】:

【参考方案2】:

我点击了这个链接

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

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

【讨论】:

以上是关于将字符串从 C# 发送到 C++ dll 文件的主要内容,如果未能解决你的问题,请参考以下文章

将字符串从 C# 发送到 C++

将数据从 C++ 发送到 C# [重复]

在非托管 C++ DLL 和托管 C# UI 之间发送信息

如何使用 DLLImport 将字符串从 C# 传递到 C++(以及从 C++ 到 C#)?

从 C++ DLL 在 C# 中触发事件

如何使用 pinvoke 将图像从 c 或 c++ 发送到 C#