使用 DLL 将 C# StringBuilder / string 传递给 C++ char*
Passing C# StringBuilder / string to C++ char* using DLL
我一直在混合使用 C# GUI 和 C++ 代码来处理套接字连接。
问题是将字符串传递给 C++ char* DLL 会导致它在 C++ class(?).
中更改其值
我有一个 class 用于处理套接字,它被稍微改变以查找错误原因和一些让我到达 class 本身的函数:
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 header 代码中看到它,它应该获取 mySock object 的 IP 地址并将其写入 C# 文本框。
如果是硬编码的 IP 地址,它确实会显示该 IP 地址,但如果从 C# 传递 IP 地址,它 returns 只是一个未填充的矩形符号,我什至无法粘贴这里。
这是链接和使用 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
的值没有正确传递或在其他地方更改。
希望我把问题说得足够清楚,因为在与上面的代码打架后我已经差不多 out-of-logic 了。
主要问题出在您的 C++ 构造函数中:
mySock::mySock(char* inIP, int inPort)
{
ipAddress = inIP;
port = inPort;
}
这里你复制了一个指向字符数组的指针。这就要求只要你需要使用ipAddress
,这个指针就一直有效。从 C# 编组的字符串仅在 p/invoke 调用期间有效。
确实,即使您仅在 C++ 中使用此 class,它的设计也是行不通的。您的 class 需要获取字符串的副本而不是指针的副本。
解决方案是让您的 C++ 代码独占使用 std::string
。让 class 负责内存管理和复制。仅在 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 而言,您误用了 StringBuilder
和 char*
。当您有一个由调用者修改的调用者分配的缓冲区时,它们会配对。对于传递到 C++ 代码的字符串,使用 string
和 const char*
。
[DllImport("...", CallingConvention = CallingConvention.Cdecl)]
public static extern IntPtr mySock_Create(string IP, int prt);
我一直在混合使用 C# GUI 和 C++ 代码来处理套接字连接。 问题是将字符串传递给 C++ char* DLL 会导致它在 C++ class(?).
中更改其值我有一个 class 用于处理套接字,它被稍微改变以查找错误原因和一些让我到达 class 本身的函数:
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 header 代码中看到它,它应该获取 mySock object 的 IP 地址并将其写入 C# 文本框。
如果是硬编码的 IP 地址,它确实会显示该 IP 地址,但如果从 C# 传递 IP 地址,它 returns 只是一个未填充的矩形符号,我什至无法粘贴这里。
这是链接和使用 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
的值没有正确传递或在其他地方更改。
希望我把问题说得足够清楚,因为在与上面的代码打架后我已经差不多 out-of-logic 了。
主要问题出在您的 C++ 构造函数中:
mySock::mySock(char* inIP, int inPort)
{
ipAddress = inIP;
port = inPort;
}
这里你复制了一个指向字符数组的指针。这就要求只要你需要使用ipAddress
,这个指针就一直有效。从 C# 编组的字符串仅在 p/invoke 调用期间有效。
确实,即使您仅在 C++ 中使用此 class,它的设计也是行不通的。您的 class 需要获取字符串的副本而不是指针的副本。
解决方案是让您的 C++ 代码独占使用 std::string
。让 class 负责内存管理和复制。仅在 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 而言,您误用了 StringBuilder
和 char*
。当您有一个由调用者修改的调用者分配的缓冲区时,它们会配对。对于传递到 C++ 代码的字符串,使用 string
和 const char*
。
[DllImport("...", CallingConvention = CallingConvention.Cdecl)]
public static extern IntPtr mySock_Create(string IP, int prt);