C#调用C函数库,如何向char**类型的参数传递数据?

C#调用C函数库,如何向char**类型的参数传递数据?

在C#调用openGL函数时,有下面这个不得不使用的函数:

// C语言:为shader(代号为name)指定其源代码codes
void glShaderSource(GLuint name, GLsizei count, char** codes, GLint* lengths);

其对应的C#声明和实现如下:

// byte** in C# matches char** in C
public static unsafe void glShaderSource(GLuint name, GLsizei count, byte** codes, GLint* lengths) {
    var context = SoftGL.GetCurrentContextObj();
    if (context == null) { return; }

    if (name == 0) { context.lastErrorCode = (uint)(ErrorCode.InvalidValue); return; }
    if (!context.name2Shader.TryGetValue(name, out var shader)) { context.lastErrorCode = (uint)(ErrorCode.InvalidOperation); return; }
    if (count < 0) { context.lastErrorCode = (uint)(ErrorCode.InvalidValue); return; }

    var builder = new System.Text.StringBuilder();
    for (int i = 0; i < count; i++) {
        var str = codes[i];//每个codes[i]都代表一个string,是源代码的一部分
        var length = lengths[i];//lengths[i]是codes[i]这个string的字符数量
        for (int j = 0; j < length; j++) {
            var c = (char)str[j];//将byte转换为char
            builder.Append(c);
        }
        builder.AppendLine();
    }
    shader.Code = builder.ToString();
}

这里要注意,C语言中的char是1个字节,而C#中的char是2个字节,C#中的byte才是与C语言中的char对应的类型。
所以,必须用byte**表示C语言函数参数中的char**

在调用此函数时,要将string[]转换为byte**,代码如下:

public static unsafe Shader? Create(string[] sourceSegments) {
    ...
    var codes = stackalloc byte*[sourceSegments.Length];
    for (int i = 0; i < sourceSegments.Length; i++) {
        // 将string复制到非托管内存
        codes[i] = (byte*)Marshal.StringToHGlobalAnsi(sourceSegments[i]);
    }
    var lengths = stackalloc GLint[sourceSegments.Length];
    for (int i = 0; i < sourceSegments.Length; i++) {
        lengths[i] = sourceSegments[i].Length;
    }
    gl.glShaderSource(shaderId, sourceSegments.Length, codes, lengths);
    // 释放非托管内存
    for (int i = 0; i < sourceSegments.Length; i++) { Marshal.FreeHGlobal((IntPtr)codes[i]); }
    ...
}

这里要注意,必须使用Marshal.StringToHGlobalAnsi(),而不是Marshal.StringToHGlobalAuto()Marshal.StringToHGlobalUni()

当然,可以用IntPtr*代替byte**

posted @ 2025-06-10 13:37  BIT祝威  阅读(39)  评论(0)    收藏  举报