C#:Marshall 结构数组,其字符串从 Unity 中的 C++/ObjectiveC 到 C#

标签 c# c++ objective-c unity3d marshalling

我正在尝试将 ObjectiveC/C++ 创建的结构数组与字符串转换为 C#,但我没有成功。大多数时候 C# 编码找不到“值”字符串字段。这是我迄今为止最好的:

C#结构:

private struct Entry
{
    public string Key;
    public string Value;
}

C# 代码
public Dictionary<string, string> Convert()
{
    var dic = new Dictionary<string, string>();

    getEntries(NativeInstance, out IntPtr pUnmanagedStringArray, out int keysCount);

    IntPtr[] pIntPtrArray = new IntPtr[keysCount];
    Entry[] entriesInDictionary = new Entry[keysCount];

    // I'm not really sure I've done this correctly?
    Marshal.Copy(pUnmanagedStringArray, pIntPtrArray, 0, keysCount);

    for (int i = 0; i < keysCount; i++)
    {
        Debug.Log("Iter " + i);

        // I'm not really sure I've done this correctly?
        entriesInDictionary[i] = Marshal.PtrToStructure<Entry>(pIntPtrArray[i]);
    }

    Marshal.FreeHGlobal(pUnmanagedStringArray); // Free native malloc for array

    foreach (var entry in entriesInDictionary)
    {
        Debug.Log("Entry" + entry.Key);
        Debug.Log("Value" + entry.Value);

        //dic.Add(entry.key, entry.value);
    }

    return dic;
}

C++/ObjectiveC
struct Entry {
    const char* key;
    const char* value;
};

void getEntries(NSDictionary* dictionary, const Entry* &_entries, int &size) {
    int count = (int) [dictionary count];

    Entry* entries = (Entry*) malloc(count * sizeof(Entry) );

    int i = 0;
    for(id key in dictionary) {
        id value = [dictionary objectForKey:key];

        entries[i].key = Utils::mallocCharStr(key); // malloc char* from NSString.
        entries[i].value = Utils::mallocCharStr(value); // malloc char* from NSString.

        ++i;
    }

    _entries = entries;
    size = count;
}

在这一点上我很迷茫,我尝试了不同的组合。此外,我已经为两个字符串尝试了 IntPtr 结构,但是我确定“值”指针为 0。

有任何想法吗?

最佳答案

我发现了这个问题。我的问题是我如何创建/读取结构数组。在最初的 C# 实现中,预期给定数组是指向结构的指针数组,但 native 代码使用结构本身创建了一个数组。

这个问题可以用两种不同的方法来解决。更新 C++ 代码以创建指向结构的指针数组:

void getEntries(NSDictionary* dictionary, Entry** &_entries, int &size) {
    int count = (int) [dictionary count];

    // Array of pointers! Structs will be malloc-ed individually on the loop.
    Entry** entries = (Entry**) malloc(count * sizeof(Entry*) );

    int i = 0;
    for(id key in dictionary) {
        id value = [dictionary objectForKey:key];

        // It creates a pointer to the struct
        entries[i] = (Entry*) malloc(sizeof(Entry));

        entries[i]->key = Utils::mallocCharStr(key);
        entries[i]->value = Utils::mallocCharStr(value);

        ++i;
    }

    _entries = entries;
    size = count;
}

C# 的处理方式是:
public Dictionary<string, string> Convert()
{
    var dic = new Dictionary<string, string>();

    getEntries(NativeInstance, out IntPtr pUnmanagedArray, out int keysCount);

    IntPtr[] pIntPtrArray = new IntPtr[keysCount];

    // This was the original problem.
    // Now it copies the native array pointers to individual IntPtr. Which now they point to individual structs.
    Marshal.Copy(pUnmanagedArray, pIntPtrArray, 0, keysCount);

    for (int i = 0; i < keysCount; i++)
    {
        Entry entry = Marshal.PtrToStructure<Entry>(pIntPtrArray[i]); // Magic!
        dic.Add(entry.Key, entry.Value);

        Marshal.FreeHGlobal(pIntPtrArray[i]); // Free the individual struct malloc
    }

    Marshal.FreeHGlobal(pUnmanagedArray); // Free native array of pointers malloc.

    return dic;
}

==

另一种可能的解决方案是保持 C++ 与原始问题相同。所以这意味着 native 代码创建了一个结构数组(不是指针)。但是随后需要更新 C# 代码 正确偏移数组中的每个项目 .
public Dictionary<string, string> Convert()
{
    var dic = new Dictionary<string, string>();

    getEntries(NativeInstance, out IntPtr pUnmanagedArray, out int keysCount);

    // Note that we don't use Marshal.Copy(...).
    // Every item in the array has an memory offset of the size of the struct, rather than the size of the pointer to a struct.

    for (int i = 0; i < keysCount; i++)
    {
        // "Selects" the nth structure by offseting the original pointer element:
        IntPtr pCurrent = pUnmanagedArray + i * Marshal.SizeOf(typeof(Entry));

        Entry entry = Marshal.PtrToStructure<Entry>(pCurrent);

        dic.Add(entry.Key, entry.Value);
    }

    // It only frees the array of struct itself because it contains the structs themselves.
    Marshal.FreeHGlobal(pUnmanagedArray);

    return dic;
}

请注意,执行自动编码(marshal)处理时会自动释放结构的字符串。

关于C#:Marshall 结构数组,其字符串从 Unity 中的 C++/ObjectiveC 到 C#,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/61347724/

相关文章:

c++ - 在成员函数返回的 vector 上调用开始和结束

c++ - 如果是指针,则启用模板

ios - 使用 AutoLayout 移动 UIView 的位置

ios - UITextfield 格式 ****用户

c# - 客户关系管理 2013 : Getting Data from Organization Service with Microsoft. XRM

c# - 如何在 C# 中刷新表单?

c++ - 调整 wxDialogs 的大小,使所有文本都在对话框中

c# - 您应该在 Java/C# 中使用国际标识符吗?

javascript - jQuery 弹出窗口不显示内部带有方括号的字符串

ios - requestInterstitialAdPresentation 只工作一次