【问题标题】:How to get an IntPtr to a struct?如何将 IntPtr 获取到结构?
【发布时间】:2013-07-10 02:49:37
【问题描述】:

我有一个带有签名的方法

public int Copy(Texture texture, Rect? srcrect, Rect? dstrect)

Rect 是一个结构,但我需要允许调用者也将null(或IntPtr.Zero)传递给方法。

然后我想将其传递给带有签名的 DLL

[DllImport("SDL2.dll", CallingConvention = CallingConvention.Cdecl, EntryPoint = "SDL_RenderCopy")]
internal static extern int RenderCopy(IntPtr renderer, IntPtr texture, IntPtr srcrect, IntPtr dstrect);

我希望我可以做以下事情:

return SDL.RenderCopy(_ptr, texture._ptr, srcrect.HasValue ? (IntPtr)srcrect.Value : IntPtr.Zero, dstrect.HasValue ? (IntPtr)dstrect.Value : IntPtr.Zero);

但我不能这样转换结构。还有其他方法可以让我获得IntPtr 吗?


另一种方法是创建 4 个重载:

  • ref Rect, ref Rect
  • IntPtr, IntPtr
  • ref Rect, IntPtr
  • IntPtr, ref Rect

如果我需要传递超过 2 个结构指针,这可能会变得更加混乱。


我想出了一个解决方案,但我对此有一些疑问:

public int Copy(Texture texture, Rect? srcrect=null, Rect? dstrect=null)
{
    return SDL.RenderCopy(_ptr, texture._ptr, srcrect.HasValue ? StructToPtr(srcrect) : IntPtr.Zero, dstrect.HasValue ? StructToPtr(dstrect) : IntPtr.Zero);
}

private static IntPtr StructToPtr(object obj)
{
    var ptr = Marshal.AllocHGlobal(Marshal.SizeOf(obj));
    Marshal.StructureToPtr(obj, ptr, false);
    return ptr;
}

如果我使用了ref Rect,我就不必为该结构分配内存——这与此有何不同?


我做了一些实验。 ref Rect 解决方案的运行速度与 converting a Rect to an IntPtr 生成 IntPtr for a Rect 的速度大致相同,这让我怀疑 C# 正在做某事当您使用 refs 时,引擎盖下非常相似。一旦我将其设为Rect? 并将条件逻辑添加到方法中,它的运行速度就会慢 50%……所以 4-overload 路线可能是最快的。然而,我们所说的 100K 迭代需要 100-150 毫秒,这意味着该方法本身非常便宜,这可能就是条件语句具有如此显着影响的原因。因此,我坚持使用我的自定义 StructToPtr 解决方案,因为它是最简单的解决方案。

【问题讨论】:

  • 您的标题措辞不当。您不能将结构转换为 IntPtr。但是,您可以获得指向结构的指针。
  • @JimMischel:是的。我并没有考虑真正“转换”它,我在想一些类似于演员阵容的东西;在 C 语言中,您可以在变量前添加 & 以获取其地址。
  • 使用Marshal.AllocHGlobal 如上图所示的StructToPtr,会给内存泄漏带来重大风险。 AllocHGlobal 分配的内存必须使用Marshal.FreeHGlobal 手动释放。显示的 Copy 方法不会这样做。

标签: c# interop


【解决方案1】:

你想使用Marshal.StructureToPtr

您还必须为结构分配和释放内存。

可以在http://www.developerfusion.com/article/84519/mastering-structs-in-c/找到有关该主题的好博客

【讨论】:

  • 需要3个参数。看来我必须为它预先分配内存。
  • 是的,很抱歉。我编辑了答案并将您指向了一个博客。
  • 做了更多搜索,我认为可以在stackoverflow.com/questions/1049623/…找到更好的解决方案
  • The solution 发布的内容与我的问题的中间部分相同——创建多个重载;一个接受IntPtr,另一个接受ref。当你在同一个函数中需要多个这些时,它会变得很麻烦;需要 2^n 次重载。
【解决方案2】:

这就是我所拥有的,我正在使用来自 M$ DLL 的 API,原型是这样的:

HRESULT C_API(LPCWSTR name, PSTRUCT_WHATEVER *ppStruct);

结构是这样定义的:

typedef struct whatever {
    LPCWSTR x;
    LPCWSTR y;
}

在 C# 中,我定义了以下内容:

[StructLayout(LayoutKind.Sequential)]
public class WHATEVER {
    public IntPtr x;
    public IntPtr y;
}

[DllImport("msdll.dll", SetLastError=false, CharSet=CharSet.Unicode)]
    public static extern long C_API(String name, out IntPtr ppStruct);

使用它:

IntPtr s;           
long HR = C_API("myname", out s);

WHATEVER pInfo = (WHATEVER) Marshal.PtrToStructure(s, typeof(WHATEVER));

String mystring = Marshal.PtrToStringAuto(pInfo.x);

此时mystring = "这是一个字符串";

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2011-10-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-07-26
    • 2014-09-07
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多