【问题标题】:C to C# interop: How to handle this methodC 到 C# 互操作:如何处理此方法
【发布时间】:2011-04-22 19:50:37
【问题描述】:

C 声明:

struct t_name
{
    char first_name[128];
    char nickname[128];
    int32_t words[7];
    uint16_t parts_of_speech[7];
    uint32_t language;
    bool has_name;
};

char* Translation_TranslateNameEnglish(DFHackObject* trans, const t_name* name);

我在 C# 中是如何做到的:

[StructLayout(LayoutKind.Sequential), Serializable]
public struct DFName
{
    [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 128)]
    public string FirstName;
    [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 128)]
    public string NickName;
    [MarshalAs(UnmanagedType.ByValArray, SizeConst = 7)]
    public int[] Words;
    [MarshalAs(UnmanagedType.ByValArray, SizeConst = 7)]
    public ushort[] PartsOfSpeech;
    public uint Language;
    public bool HasName;
}

[DllImport(DllName)]
public static extern string Translation_TranslateNameEnglish(IntPtr ptr, ref DFName name);

呼唤:

DFHack.Translation_TranslateNameEnglish(translation, ref name)

使用 IntPtr 作为第一个参数是没有问题的,我在类似的调用中也有这样的工作。 DFName 结构在另一个调用中填充,它包含有效数据。 然而,不起作用的是对 TranslateNameEnglish 的调用。当该行执行时,我收到一条错误消息“尝试读取或写入受保护的内存。这通常表明其他内存已损坏。” 我做错了什么?

我忘了提及哪些可能很重要:DFName 结构是作为另一个结构的一部分检索的。

[StructLayout(LayoutKind.Sequential), Serializable]
public struct DFCreature
{
// Snip
    public DFName Name;
// Snip
}

用方法检索到的

public static extern int Creatures_ReadCreature(IntPtr ptr, uint index, out DFCreature creature);

【问题讨论】:

  • 请使用相同的电子邮件/OpenID 登录,这样您将作为同一个人进行线程化,并且您将能够编辑您的答案等。

标签: c# interop


【解决方案1】:

确保您的“C”代码中的sizeof(t_name) 与您的C# 代码中的Marshal.SizeOf(typeof(DFName)) 相同。如果不一样,您需要找出原因 - 特别是查看 C 代码中的打包选项,以及 p/invoke 签名中的 Ansi/Unicode 选项。

更新:实际上,在这里检查一下,看起来没问题,只要 MS 'C' 编译器使用默认打包选项运行,但绝对值得在您的环境中检查以防发生更改。

【讨论】:

    【解决方案2】:

    是的,“试图读取或写入受保护的内存。这通常表明其他内存已损坏。”是预期的结果。发生这种情况是因为您尝试从函数内返回字符串值。在这种情况下,.NET 框架创建字符串对象并尝试释放内存,您可以在其中使用 CoTaskMemFree 函数从函数中获取字符串。据我了解,您没有使用 CoTaskMemAlloc 分配此内存,因此您的例外是正确的。为避免这种情况,您应该更改 C# 函数原型以返回 IntPtr:

    [DllImport(DllName)]
    public static extern IntPtr Translation_TranslateNameEnglish(IntPtr ptr, ref DFName name);
    

    你可以像下面这样:

    string result = Marshal.PtrToStringAnsi(DFHack.Translation_TranslateNameEnglish(translation, ref name);
    

    [编辑]
    它也可能对您有用 - Marshaling unmanaged char** to managed string[]

    【讨论】:

    • 不幸的是同时使用你的方法和文章中描述的方法我得到了同样的异常
    • @Patrick:嗯,name 有哪些类型?尝试使用 StringBuilder 。如果这没有帮助,我现在没有更多的假设
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-05-12
    • 2021-10-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多