char** DllImport 失败

Posted

技术标签:

【中文标题】char** DllImport 失败【英文标题】:char** DllImport fails 【发布时间】:2016-05-30 18:05:32 【问题描述】:

我想 DllImport 以下函数。尽管如此,“ret”返回真,但我的字符串数组似乎是空的,所以我想我可能需要一些封送处理。欢迎任何提示!在此先感谢:)

C 函数:

bool getBootLog(char **a1);

以下代码用于测试,不能正常工作。

DllImport:

[DllImport("ext.dll")]
public static extern bool getBootLog(string[] bootLog);

当前代码:

        string[] bootLog = new string[1024 * 1024];
        bool ret = getBootLog(bootLog);

        foreach (string s in bootLog)
        
            Debug.WriteLine(s);
        

另外 2 次尝试无效:

var ptr = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(IntPtr)));
try

    getBootLog(out ptr);
    var deref1 = (string)Marshal.PtrToStringAnsi(ptr);
    Debug.WriteLine(deref1);

finally

    Marshal.FreeHGlobal(ptr);


var ptr2 = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(IntPtr)));
try

    getBootLog(out ptr2);
    var deref1 = (IntPtr)Marshal.PtrToStructure(ptr2, typeof(IntPtr));
    var deref2 = (string[])Marshal.PtrToStructure(deref1, typeof(string[]));
    Debug.WriteLine(deref2);

finally

    Marshal.FreeHGlobal(ptr2);

莫森的想法:

[DllImport("Ext.dll")]
public static extern bool getBootLog(StringBuilder bootLog);

try

    int bufferSize = 50;
    StringBuilder bootLog = new StringBuilder(" ", bufferSize);
    Debug.WriteLine("Prepared bootLog...");
    getBootLog(bootLog);
    Debug.WriteLine("Bootlog length: " + bootLog.Length);
    string realString = bootLog.ToString();
    Debug.WriteLine("Bootlog: " + realString);

catch(Exception ex)

    Debug.WriteLine("Xception: " + ex.ToString());

结果:

已准备好 bootLog... Bootlog 长度:0 Bootlog:

【问题讨论】:

你能把问题说得更具体一点吗? string[] 和 char** 的类型不一样。您需要通过 char** 调用 getBootLog。 这个函数很难从 C 程序中调用,当你 pinvoke 时它并没有变得更好。参数必须是out IntPtr。接下来是盲目的猜测,但它必须从 Marshal.PtrToStringAnsi() 开始。您很可能会发生内存泄漏。 @iwein:我需要一种 C# 中的工作方式来调用 C 函数。上面显示的方式只是部分工作。 @metaDom 我要求您编辑问题以使其更清楚...... 【参考方案1】:

修正声明:

[DllImport("ext.dll", CharSet = CharSet.Ansi)]
public static extern bool getBootLog(ref IntPtr bootLogPtr);

在代码的编辑版本中尝试的行看起来不正确。

var ptr = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(IntPtr)));

真的,应该指定缓冲区的大小。

var ptr = Marshal.AllocHGlobal(100); // Set it to 100 bytes, maximum!

将长度写入ptr,作为它的 C 风格以空字符结尾的字符串。

Marshal.WriteByte(ptr, 100, 0);

然后调用我脑海中的调用:

IntPtr ptrBuf = ptr;
getBootLog(ref ptrBuf);

ptrBuf的缓冲区内容复制到字符串变量中:

string sBootLog = Marshal.PtrToStringAnsi(ptrBuf);

清理非托管内存内容:

Marshal.FreeHGlobal(ptr);

【讨论】:

我有机会影响“日志”并覆盖其内容。我已将其设置为“XYZABC”。如果我从您的代码中打印“string sBootLog”,它包含“X”。所以它可能有效,但不完全......到目前为止,“ABCXYZ”的第二次测试也有效 - 打印了“A”。 好的,问题在我这边。原型的错误解释:它不是“bool getBootLog(char **a1)”而是“int getBootLog(wchar_t **a1)”。完成后我会发布固定代码...

以上是关于char** DllImport 失败的主要内容,如果未能解决你的问题,请参考以下文章

调用从 c# 返回 char* 的 c++ dll 函数。无法使用 DllImport()

C# 中的字符 * 编组

如何处理失败的 DllImport?

为啥 `[DllImport]` 会以 `RtlSecureZeroMemory` 的入口点失败,即使它是一个有据可查的入口点?

DllImport Unmanaged, Non .NET Dll to .NET Project 表示 Char * 和 Void __StdCall

在DllImport中使用Unicode字符串和用Rust编写的DLL