调用从 c# 返回 char* 的 c++ dll 函数。无法使用 DllImport()
Posted
技术标签:
【中文标题】调用从 c# 返回 char* 的 c++ dll 函数。无法使用 DllImport()【英文标题】:Calling c++ dll functions returning char* from c#. Cannot use DllImport() 【发布时间】:2013-02-19 08:44:02 【问题描述】:我有一个带有 2 个函数的 c++ dll
const char* getVersionText1(void);
void getVersionText2(const char** res);
这两个函数都返回一个描述产品版本的字符串。第一个函数将其作为 const char* 返回(这意味着它在内部分配和处理定位它),第二个函数获取指向 char* 的指针并将其设置为指向描述版本的 char*。
我想从 C# 调用这些函数并显示文本。 我不能使用 [dllimport ...] 样式,因为调用函数的顺序很重要。我首先调用构造函数而不是 getVersion,最后调用析构函数。所以dll必须先加载到内存中。
能否请您发布几行代码,打印两个函数的文本。 我是 C# 新手,如果您发现我的代码有问题,非常抱歉 我试过了
static class NativeMethods
[DllImport("kernel32.dll")]
public static extern IntPtr LoadLibrary(string dllToLoad);
[DllImport("kernel32.dll")]
public static extern IntPtr GetProcAddress(IntPtr hModule, string procedureName);
[DllImport("kernel32.dll")]
public static extern bool FreeLibrary(IntPtr hModule);
class Program
// Define function pointers to entires in DLL
[UnmanagedFunctionPointer(CallingConvention.Cdecl)] private delegate IntPtr getVersionText1();
[UnmanagedFunctionPointer(CallingConvention.Cdecl)] private delegate void getVersionText2(IntPtr );
static void Main(string[] args)
// Pointers to functions of DLL.
getVersionText1 f1;
getVersionText2 f2;
IntPtr pDll = NativeMethods.LoadLibrary("p:\\my.dll");
IntPtr pAddressOfFunctionToCall;
pAddressOfFunctionToCall = NativeMethods.GetProcAddress(pDll, "getVersionText1 ");
f1 = (getVersionText1)Marshal.GetDelegateForFunctionPointer(pAddressOfFunctionToCall, typeof(getVersionText1));
pAddressOfFunctionToCall = NativeMethods.GetProcAddress(pDll, "getVersionText2 ");
f2 = (getVersionText2)Marshal.GetDelegateForFunctionPointer(pAddressOfFunctionToCall, typeof(getVersionText2));
// Here I don't know what to do. It doesn't work ????
IntPtr verText = f1();
String s = Marshal.PtrToStringAuto(verText);
Console.WriteLine(s); // Doesnt work
// And how to use the second function. How do I sent a pointer to char* ???
【问题讨论】:
您确定getVersionText1
和getVersionText2
是实际导出的函数名称吗? C++ 名称修改通常会更改您导出的名称。您可以使用任何 DLL 导出查看器检查导出的名称。
我确定。我用 C++ 编写了 DLL
您可以制作一个托管的 C++/CLI 包装 DLL 来充当 C++ 和 C# 代码之间的通信器
【参考方案1】:
如果 C++ 库函数返回 char*
,C# 代码会将其视为 IntPtr
,Marshal.PtrToStringAnsi()
会将其转换为 C# string
。
IntPtr verText = f1();
string s = Marshal.PtrToStringAnsi(verText);
Console.WriteLine(s);
【讨论】:
【参考方案2】:这是一种拨打电话的方法。可能有更短的替代方案可以自动执行 utf8/字符串转换。
[DllImport("mydll.dll")]
public static extern IntPtr getVersionText1(void);
public string getVersionTextConst()
IntPtr ptr = getVersionText1();
// assume returned string is utf-8 encoded
return PtrToStringUtf8(ptr);
[DllImport("mydll.dll")]
public static extern void getVersionText2(out IntPtr res);
public string getVersionTextConst()
IntPtr ptr;
getVersionText2(ptr);
// assume returned string is utf-8 encoded
String str = PtrToStringUtf8(ptr);
// call native DLL function to free ptr
// if no function exists, pinvoke C runtime's free()
return str;
private static string PtrToStringUtf8(IntPtr ptr) // aPtr is nul-terminated
if (ptr == IntPtr.Zero)
return "";
int len = 0;
while (System.Runtime.InteropServices.Marshal.ReadByte(ptr, len) != 0)
len++;
if (len == 0)
return "";
byte[] array = new byte[len];
System.Runtime.InteropServices.Marshal.Copy(ptr, array, 0, len);
return System.Text.Encoding.UTF8.GetString(array);
【讨论】:
使用 getVersionText1() 有效。谢谢!但是我无法定义 getVersionText2(IntPtr* res) 因为编译器尖叫着不允许在安全上下文中使用指针。 WTF 是安全上下文???我看到您使用了“不安全”关键字,但我不确定是否允许我使用它,因为整个应用程序都是安全的 您可以从解决方案资源管理器中启用不安全代码的使用 -> 属性 -> 构建 -> 允许不安全代码 我不能使用 unsafe,因为我的 DLL 嵌入在第三方应用程序中,我不能强迫客户使用 unsafe 编译。但解决方案是关键字 'out IntPtr' 而不是 'IntPtr*' @DanielHsH 谢谢,你的建议好多了。我已经相应地更新了我的答案。【参考方案3】:如果您需要更快的速度,您也可以进行不安全读取:
unsafe string GetString(IntPtr ptr) => new string((char*)ptr); // local function
IntPtr charPtr = f1();
var s = GetString(charPtr);
当你添加unsafe
方法时,VS 会询问你是否要在项目中允许不安全代码:回答是。或者手动“项目属性/构建/允许不安全代码”。
【讨论】:
以上是关于调用从 c# 返回 char* 的 c++ dll 函数。无法使用 DllImport()的主要内容,如果未能解决你的问题,请参考以下文章
从 C# 调用 C++ dll。 “无法封送'返回值':托管/非托管类型组合无效。”