使用 DLL 将 C# StringBuilder / 字符串传递给 C++ char*

Posted

技术标签:

【中文标题】使用 DLL 将 C# StringBuilder / 字符串传递给 C++ char*【英文标题】:Passing C# StringBuilder / string to C++ char* using DLL 【发布时间】:2016-12-28 23:36:52 【问题描述】:

我一直在将 C# GUI 与 C++ 代码混合用于套接字连接处理。 问题在于将字符串传递给 C++ char* DLL 会导致它在 C++ 类中更改其值(?)。

我有一个用于处理套接字的类,它进行了一些更改以查找错误原因和一些函数让我进入该类本身:

extern "C"  __declspec(dllexport)

    class mySock
    

        int port;
        SOCKET littleSock;

        public:
            char* ipAddress;
            mySock() ;
            mySock(char* inIP, int inPort);
            ~mySock();
            int initialize();
            int sendMsg(char* Msg);
            //int addition(int a, int b);

    ;


extern "C"  __declspec(dllexport) mySock* mySock_Create(char* IP, int prt);
extern "C"  __declspec(dllexport) mySock* mySock_Create1(); 
extern "C"  __declspec(dllexport) int mySock_initialize(mySock* inSocket); 
extern "C"  __declspec(dllexport) int mySock_sendMsg(mySock* inSocket,char* Msg); 
extern "C"  __declspec(dllexport) void mySock_delete(mySock* inSocket); 
extern "C"  __declspec(dllexport) void CopyStr(char* str1, mySock* inSocket)
    
        strcpy_s(str1, strlen(str1), inSocket->ipAddress);
    ;

问题出现在使用 mySock_Create 的某个地方。

基本上是这样的:

mySock* mySock_Create(char* IP, int prt)

    return new mySock(IP, prt);

调用构造函数:

mySock::mySock(char* inIP, int inPort)

    ipAddress = inIP;
    port = inPort;

稍后ipAddress 与inet_addr 一起使用并连接到远程服务器。 无论如何,上面的代码不允许我连接到服务器,但如果我只是像这样硬编码 IP 地址:

mySock* mySock_Create(char* IP, int prt)

    return new mySock("192.168.1.164", prt);

它工作正常并连接到本地服务器。

所以为了检查,我添加了 CopyStr,您可以在上面的 DLL 标头代码中看到它,它应该获取 mySock 对象的 IP 地址并将其写入 C# 文本框。

如果是硬编码的 IP 地址,它确实会显示该 IP 地址,但如果从 C# 传递一个 IP 地址,它只会返回一个未填充的矩形符号,我什至无法在此处粘贴。

这是用于链接和使用 DLL 的 C# 代码:

[DllImport("G:\\socketstraning\\klikacz\\MyFirstDLLyay\\Debug\\MyFirstDLL.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern IntPtr mySock_Create(StringBuilder IP, int prt);
[DllImport("G:\\socketstraning\\klikacz\\MyFirstDLLyay\\Debug\\MyFirstDLL.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern int mySock_initialize(IntPtr value);
[DllImport("G:\\socketstraning\\klikacz\\MyFirstDLLyay\\Debug\\MyFirstDLL.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern int mySock_sendMsg(IntPtr value, string Msg);
[DllImport("G:\\socketstraning\\klikacz\\MyFirstDLLyay\\Debug\\MyFirstDLL.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern void mySock_delete(IntPtr value);
[DllImport("G:\\socketstraning\\klikacz\\MyFirstDLLyay\\Debug\\MyFirstDLL.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern IntPtr mySock_Create1();
[DllImport("G:\\socketstraning\\klikacz\\MyFirstDLLyay\\Debug\\MyFirstDLL.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern void CopyStr(StringBuilder str1, IntPtr value);

private void button3_Click(object sender, EventArgs e)

    StringBuilder str = new StringBuilder("192.168.1.164", 50);
    IntPtr we = mySock_Create(str, 16999);
    StringBuilder str2 = new StringBuilder("255.255.255.255", 25);
    CopyStr(str2, we);
    textBox1.Text = str2.ToString();
    if (mySock_initialize(we) == -2)
         button3.Text = "Ups";

    mySock_delete(we);

我也尝试过使用字符串而不是 StringBuilder 或在导入选项中添加 CharSet=CharSet.Ansi。

我已经阅读了很多关于在这种情况下将字符串传递给 char* 的问题和解决方案,但我无法弄清楚为什么 ipAddress 的值没有正确传递或在其他地方发生更改。

希望我已经足够清楚地说明了问题,因为在与上面的代码斗争之后,我已经非常不合逻辑了。

【问题讨论】:

C++ 代码需要分配一个缓冲区并复制发送给它的字符串,而不仅仅是存储指针。 【参考方案1】:

主要问题在于您的 C++ 构造函数:

mySock::mySock(char* inIP, int inPort)

    ipAddress = inIP;
    port = inPort;

在这里,您可以复制指向字符数组的指针。这要求只要您需要使用ipAddress,该指针就保持有效。从 C# 编组的字符串仅在 p/invoke 调用期间有效。

确实,即使您仅从 C++ 中使用此类,它的设计也是行不通的。您的类需要获取字符串的副本而不是指针的副本。

解决方案是让您的 C++ 代码独占使用 std::string。让该类负责内存管理和复制。仅在 p/invoke 互操作边界处使用 C 字符串。

ipAddress 更改为std::string 类型并让构造函数如下所示:

mySock::mySock(const std::string inIP, const int inPort)

    ipAddress = inIP;
    port = inPort;

您仍然需要在互操作包装函数中使用 C 字符串,但这可以利用从 const char*std::string 的隐式转换。

mySock* mySock_Create(const char* IP, const int prt)

    return new mySock(IP, prt);

就 p/invoke 而言,您误用了 StringBuilderchar*。当您有一个由被调用者修改的调用者分配的缓冲区时,它们是配对的。对于传入 C++ 代码的字符串,使用stringconst char*

[DllImport("...", CallingConvention = CallingConvention.Cdecl)]
public static extern IntPtr mySock_Create(string IP, int prt);

【讨论】:

非常感谢!最后,这是我一直在寻找的答案(或者我猜是 ATM)我确实明白我的代码问题出在哪里。 :) 你是否建议简单地使用字符串而不是 char* 并且我需要一个 char* 使用 c_str() 方法?现在我正在将字符串从 c# 传递到 c++ 中,但我已经将此答案标记为最有帮助 字符串从 C# 传递到 C++,因此您永远不需要使用 c_str()。有一个从 const char* 到 std::string 的隐式转换,因此我的袜子创建的包装函数可以接收 const char* ,然后在调用构造函数时使用该隐式转换。

以上是关于使用 DLL 将 C# StringBuilder / 字符串传递给 C++ char*的主要内容,如果未能解决你的问题,请参考以下文章

如何安全地将字符串引用从 c# 传递到 c++?

C# 调用C++ DLL,而c++函数的有一个参数需要是null,该怎么传递?

C#使用ObjectPool提高StringBuilder性能

C# 将 char* 编组到 StringBuilder 总是得到空字符串

C# stringbuilder为啥高效

将可写 StringBuilder 数组从 C# 传递给 C++