如何从 C# 项目中的 C++ dll 导入导出的 char*
How to import an exported char* from C++ dll in C# project
我正在尝试打开从我的 C# 项目中的 Dll 导出的 char*。
由于代码太多,我将编写一个与我正在处理的代码类似的简短代码。
C++:
__declspec(dllexport) typedef struct Kid {
char* _name;
int _age;
int _grade;
} Kid;
Kid get_default_kid()
{
char name[] = { "Quinn" };
return Kid{
name, 2,7
};
}
extern "C" __declspec(dllexport) Kid get_default_kid();
C#:
public struct Kid
{
public string _name;
public int _age;
public int _grade;
}
private const string Dll2 = @"C:\Users\Me\source\repos\Tom\x64\Release\JerryDll.dll";
[DllImport(Dll2, CallingConvention = CallingConvention.Cdecl)]
private static extern Kid get_default_kid();
public static void Main(string[] args){
Kid defaultKid = get_default_kid();
Console.WriteLine(defaultKid._name);
Console.WriteLine(defaultKid._age);
Console.WriteLine(defaultKid._grade);
}
此代码在控制台中写入了一些随机字符,如“♠”,而不是从 dll 中导出的名称。
我尝试过的:
尝试将 char*
导入为 IntPtr
然后使用以下方式读取它:
Marshal.PtrToStringAnsi()/BSTR/Auto/Uni
和 Marshal.ReadIntPtr
在尝试使用上面的行读取它之前。
从那以后,我尝试在 C# 中将字符串转换为 UTF-8。
在 google 中搜索了很多。
字符串的默认编组行为是宽字符串,例如LPWSTR
。您将不得不添加一个封送处理指令来告诉 .NET 封送处理程序您真正提供的是一个窄字符串(例如 LPSTR
或 char*
)。
阅读更多 on MSDN。
该页面上有一个与您想要的很接近的示例 - 您几乎可以只复制编组指令:
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
struct StringInfoA
{
[MarshalAs(UnmanagedType.LPStr)] public string f1;
}
所以你的是:
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public struct Kid
{
[MarshalAs(UnmanagedType.LPStr)]
public string _name;
public int _age;
public int _grade;
}
一般来说,当我们谈到 C/C++ 和 C# 之间的封送处理字符串(或其他分配的内存)时,最重要和最复杂的问题是:如何释放内存?如果可能的话,最简单的解决方案是从 C/C++ 端导出一个或多个释放器方法。我在这里举一些例子:
请注意,您不能
public struct Kid
{
public string _name;
public int _age;
public int _grade;
}
你会得到一个错误。 .NET 封送拆收器不想封送具有 return 值的非 blittable 结构(因此 struct
具有复杂类型,如 string
s)。出于这个原因,我使用 IntPtr
然后手动将 char*
编组为 string
.
C++:
extern "C"
{
const char* pstrInternal = "John";
typedef struct Kid
{
char* _name;
int _age;
int _grade;
};
// WRONG IDEA!!! HOW WILL THE "USER" KNOW IF THEY SHOULD
// DEALLOCATE OR NOT?
__declspec(dllexport) Kid get_default_kid_const()
{
// MUSTN'T DEALLOCATE!!!
return Kid { (char*)pstrInternal, 2,7 };
}
__declspec(dllexport) Kid get_a_kid()
{
// this string must be freed with free()
char* pstr = strdup(pstrInternal);
return Kid { pstr, 2,7 };
}
__declspec(dllexport) void free_memory(void* ptr)
{
free(ptr);
}
__declspec(dllexport) void free_kid(Kid* ptr)
{
if (ptr != NULL)
{
free(ptr->_name);
}
}
}
C#:
public struct Kid
{
public IntPtr _namePtr;
public int _age;
public int _grade;
public string _name { get => Marshal.PtrToStringAnsi(_namePtr); }
}
[DllImport("CPlusPlusSide.dll", CallingConvention = CallingConvention.Cdecl)]
private static extern Kid get_default_kid_const();
[DllImport("CPlusPlusSide.dll", CallingConvention = CallingConvention.Cdecl)]
private static extern Kid get_a_kid();
[DllImport("CPlusPlusSide.dll", CallingConvention = CallingConvention.Cdecl)]
private static extern void free_memory(IntPtr ptr);
[DllImport("CPlusPlusSide.dll", CallingConvention = CallingConvention.Cdecl)]
private static extern void free_kid(ref Kid kid);
public static void Main(string[] args)
{
Kid kid = get_default_kid_const();
Console.WriteLine($"{kid._name}, {kid._age}, {kid._grade}");
// MUSTN'T FREE this Kid!!!
Kid kid2 = get_a_kid();
Console.WriteLine($"{kid2._name}, {kid2._age}, {kid2._grade}");
free_memory(kid2._namePtr);
Kid kid3 = get_a_kid();
Console.WriteLine($"{kid3._name}, {kid3._age}, {kid3._grade}");
free_kid(ref kid3);
}
请注意,对于那些在 C/C++ 和 C# 之间编组字符串但不知道其工作原理的人来说,这是一个痛苦的世界(随机崩溃和内存泄漏造成的痛苦)。
我正在尝试打开从我的 C# 项目中的 Dll 导出的 char*。
由于代码太多,我将编写一个与我正在处理的代码类似的简短代码。
C++:
__declspec(dllexport) typedef struct Kid {
char* _name;
int _age;
int _grade;
} Kid;
Kid get_default_kid()
{
char name[] = { "Quinn" };
return Kid{
name, 2,7
};
}
extern "C" __declspec(dllexport) Kid get_default_kid();
C#:
public struct Kid
{
public string _name;
public int _age;
public int _grade;
}
private const string Dll2 = @"C:\Users\Me\source\repos\Tom\x64\Release\JerryDll.dll";
[DllImport(Dll2, CallingConvention = CallingConvention.Cdecl)]
private static extern Kid get_default_kid();
public static void Main(string[] args){
Kid defaultKid = get_default_kid();
Console.WriteLine(defaultKid._name);
Console.WriteLine(defaultKid._age);
Console.WriteLine(defaultKid._grade);
}
此代码在控制台中写入了一些随机字符,如“♠”,而不是从 dll 中导出的名称。
我尝试过的:
尝试将 char*
导入为 IntPtr
然后使用以下方式读取它:
Marshal.PtrToStringAnsi()/BSTR/Auto/Uni
和 Marshal.ReadIntPtr
在尝试使用上面的行读取它之前。
从那以后,我尝试在 C# 中将字符串转换为 UTF-8。
在 google 中搜索了很多。
字符串的默认编组行为是宽字符串,例如LPWSTR
。您将不得不添加一个封送处理指令来告诉 .NET 封送处理程序您真正提供的是一个窄字符串(例如 LPSTR
或 char*
)。
阅读更多 on MSDN。
该页面上有一个与您想要的很接近的示例 - 您几乎可以只复制编组指令:
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
struct StringInfoA
{
[MarshalAs(UnmanagedType.LPStr)] public string f1;
}
所以你的是:
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public struct Kid
{
[MarshalAs(UnmanagedType.LPStr)]
public string _name;
public int _age;
public int _grade;
}
一般来说,当我们谈到 C/C++ 和 C# 之间的封送处理字符串(或其他分配的内存)时,最重要和最复杂的问题是:如何释放内存?如果可能的话,最简单的解决方案是从 C/C++ 端导出一个或多个释放器方法。我在这里举一些例子:
请注意,您不能
public struct Kid
{
public string _name;
public int _age;
public int _grade;
}
你会得到一个错误。 .NET 封送拆收器不想封送具有 return 值的非 blittable 结构(因此 struct
具有复杂类型,如 string
s)。出于这个原因,我使用 IntPtr
然后手动将 char*
编组为 string
.
C++:
extern "C"
{
const char* pstrInternal = "John";
typedef struct Kid
{
char* _name;
int _age;
int _grade;
};
// WRONG IDEA!!! HOW WILL THE "USER" KNOW IF THEY SHOULD
// DEALLOCATE OR NOT?
__declspec(dllexport) Kid get_default_kid_const()
{
// MUSTN'T DEALLOCATE!!!
return Kid { (char*)pstrInternal, 2,7 };
}
__declspec(dllexport) Kid get_a_kid()
{
// this string must be freed with free()
char* pstr = strdup(pstrInternal);
return Kid { pstr, 2,7 };
}
__declspec(dllexport) void free_memory(void* ptr)
{
free(ptr);
}
__declspec(dllexport) void free_kid(Kid* ptr)
{
if (ptr != NULL)
{
free(ptr->_name);
}
}
}
C#:
public struct Kid
{
public IntPtr _namePtr;
public int _age;
public int _grade;
public string _name { get => Marshal.PtrToStringAnsi(_namePtr); }
}
[DllImport("CPlusPlusSide.dll", CallingConvention = CallingConvention.Cdecl)]
private static extern Kid get_default_kid_const();
[DllImport("CPlusPlusSide.dll", CallingConvention = CallingConvention.Cdecl)]
private static extern Kid get_a_kid();
[DllImport("CPlusPlusSide.dll", CallingConvention = CallingConvention.Cdecl)]
private static extern void free_memory(IntPtr ptr);
[DllImport("CPlusPlusSide.dll", CallingConvention = CallingConvention.Cdecl)]
private static extern void free_kid(ref Kid kid);
public static void Main(string[] args)
{
Kid kid = get_default_kid_const();
Console.WriteLine($"{kid._name}, {kid._age}, {kid._grade}");
// MUSTN'T FREE this Kid!!!
Kid kid2 = get_a_kid();
Console.WriteLine($"{kid2._name}, {kid2._age}, {kid2._grade}");
free_memory(kid2._namePtr);
Kid kid3 = get_a_kid();
Console.WriteLine($"{kid3._name}, {kid3._age}, {kid3._grade}");
free_kid(ref kid3);
}
请注意,对于那些在 C/C++ 和 C# 之间编组字符串但不知道其工作原理的人来说,这是一个痛苦的世界(随机崩溃和内存泄漏造成的痛苦)。