p / invoke使用字符串数组

时间:2018-04-08 15:51:25

标签: c# pinvoke marshalling unmanaged

我从C#调用一个C dll,找不到正确的方法来编组返回的结构。

在执行编组的ConvertConfigStructToDico中,我正确地读取了第一个size_t字段,但在使用我的ConvertStringArray读取char **时崩溃了。然而我在'char **方法()'上测试后者并且它有效,所以我想知道我是否没有错误地将我的指针推进到行上的错误位置 IntPtr p = args + Marshal.SizeOf( typeof运算(ULONG));

如果有人可以就此事提供帮助,我将不胜感激......

C方法(外部方法,我不能改变它):

// By design, the returned length is always 1.
configStruct *Config_enumerate(size_t *length);

typedef struct
{
    size_t size;
    char **keys;
    char **vals;
} configStruct;

我的C#代码:

public static List<Dictionary<string, string>> GetConfig(string args = "")
{
   // By design, the returned length will always be 1.
   ulong length = 0;
   IntPtr configStructArray = Config_enumerate(ref length);
   var list = new List<Dictionary<string, string>>((int)length);

   // Correct because length is always 1.
   var dico = ConvertConfigStructToDico(configStructArray);
   list.Add(dico);

   return list;
}
[DllImport("external", CallingConvention=CallingConvention.Cdecl)]
static extern IntPtr Config_enumerate(ref ulong length);

static Dictionary<string, string> ConvertConfigStructToDico(IntPtr args)
{
    //typedef struct
    //{
    //  size_t size;
    //  char** keys;
    //  char** vals;
    //}
    //configStruct;

    // Works fine, I get the right size.
    ulong size = (ulong)Marshal.ReadInt64(args);    //size_t is ulong in Win64 and uint in x86
    // Advance pointer to next field
    IntPtr p = args + Marshal.SizeOf(typeof(ulong));
    // BUG: ConvertStringArray will incorrectly read the first string then crash (access violation). Is my point "args" pointer incorrect?
    string[] keys = ConvertStringArray(p, (uint)size);    // This frees unmanaged memory.
    p += (int)size * IntPtr.Size;
    string[] values = ConvertStringArray(p, (uint)size);  // This frees unmanaged memory.
    Marshal.FreeCoTaskMem(args);

    Dictionary<string, string> dico = new Dictionary<string, string>((int)size);
    for (int i = (int)size - 1; i >= 0; i--)
        dico[keys[i]] = values[i];
        return dico;
}

/// <summary>
/// Converts an unmanaged string array specified by its pointer, into an array of managed <see cref="String"/>, and cleans up unmanaged memory afterwards.
/// </summary>
/// <param name="args">The pointer to the unmanaged string array</param>
/// <param name="length">The number of items in the unmanaged string array</param>
/// <returns></returns>
static string[] ConvertStringArray(IntPtr args, uint length)
{
    string[] array = new string[length];
    IntPtr p = args;
    for (int i = 0; i < length; i++)
    {
        var strPtr = (IntPtr)Marshal.PtrToStructure(p, typeof(IntPtr));
        array[i] = Marshal.PtrToStringAnsi(strPtr);
        Marshal.FreeCoTaskMem(strPtr);
        p += IntPtr.Size;
    }

    Marshal.FreeCoTaskMem(args);
    return array;
}

0 个答案:

没有答案