从 p/invoke 修改结构布局

Posted

技术标签:

【中文标题】从 p/invoke 修改结构布局【英文标题】:Modify struct layout from p/invoke 【发布时间】:2012-10-28 10:30:49 【问题描述】:

我正在寻找有关更改返回/传递到 p/invoke 函数的对象的结构/类布局的最佳实践指南。我已经搜索过这个问题的答案,但也许我太累了,我没有有效地搜索。

我能想到的最简单的例子(真正的例子在这里有点太复杂了)是 GetWindowRect 之类的。

如果我想为 RECT 结构添加一些额外的属性,我应该将其添加到结构本身的定义中,还是应该切换到子类化来添加额外的属性?

是否有来自 Microsoft 或其他可靠来源的最佳做法围绕以下方法?这两种方法都违反最佳做法吗?

[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
static extern bool GetWindowRect(HandleRef hWnd, out RECT lpRect);

[StructLayout(LayoutKind.Sequential)]
public struct RECT

    public int Left;        // x position of upper-left corner
    public int Top;         // y position of upper-left corner
    public int Right;       // x position of lower-right corner
    public int Bottom;      // y position of lower-right corner

    public string Extra;    // ADDED

对比

[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
static extern bool GetWindowRect(HandleRef hWnd, out RECT lpRect);

[StructLayout(LayoutKind.Sequential)]
public class RECT

    public int Left;        // x position of upper-left corner
    public int Top;         // y position of upper-left corner
    public int Right;       // x position of lower-right corner
    public int Bottom;      // y position of lower-right corner


public class RectEx : RECT

    public string Extra;    // Added

    public RectEx(RECT r)
    
        Left = r.Left;
        Top = r.Top;
        Right = r.Right;
        Bottom = r.Bottom;
        Extra = "test";
    

【问题讨论】:

第二个示例是否使用 RECT 作为类而不是结构? 两者都不是更好。使用您喜欢的任何一个。 你不能在一个类中使用 out 关键字,这会使它成为一个 RECT**,一个指向 RECT 指针的指针。 [Out] 属性是必需的。最好不要胡乱使用 pinvoke 类型,封装它们。 很好地理解了“out”参数。就像我说的,这是一个示例,而不是我正在做的完全重复。如果不是为了出片,上课方式会起作用。汉斯,我喜欢你关于不要胡闹的评论。 我重新表述了这个问题,希望它可以重新打开。这个问题是关于最佳实践的,特别是是否应该做某事。 【参考方案1】:

这是另一种选择:这允许您维护本机功能并为您正在使用的对象提供一些安全性。

// used internally in native method
[StructLayout(LayoutKind.Sequential)]
internal struct RECT

    public int Left;        // x position of upper-left corner
    public int Top;         // y position of upper-left corner
    public int Right;       // x position of lower-right corner
    public int Bottom;      // y position of lower-right corner



// public accessible struct with extra fields 
public struct RectEx

    public int Left;        // x position of upper-left corner
    public int Top;         // y position of upper-left corner
    public int Right;       // x position of lower-right corner
    public int Bottom;      // y position of lower-right corner

    public dynamic Extra = "Extra";



public static class UnsafeNativeMethods

    //used internally to populate RECT struct
    [DllImport("user32.dll")]
    [return: MarshalAs(UnmanagedType.Bool)]
    private static extern bool GetWindowRect(HandleRef hWnd, out RECT lpRect);

    //public safe method with exception handling and returns a RectEx
    public static RectEx GetWindowRectangle(HandleRef hWnd)
    
        RECT r = new RECT();
        RectEx result = new RectEx();

        try
        
            GetWindowRect(hWnd, r);
            result.Left = r.Left;
            result.Top = r.Top;
            result.Right = r.Right;
            result.Bottom = r.Bottom;
            // assign extra fields
        
        catch(Exception ex)
        
            // handle ex
        

    return result;
    

【讨论】:

我喜欢你的建议,尽管它需要一些冗余。尽管我不确定这是否是最佳做法,但这似乎是一种合理的处理方式,并希望给你积分。谢谢! @hdrpunk,是的,这里有一些冗余,因为我写了这篇文章,我发现你可以在 REAL 结构中添加额外的字段(在这种情况下为 RECT)。您甚至可以按适合您的任何顺序获取字段,但您需要使用 LayoutKind.Explicit 以便编组器知道将非托管数据映射到正确结构字段的位置。【参考方案2】:

您还可以使用: StructLayout(LayoutKind.Explicit)

[StructLayout(LayoutKind.Sequential)]
public struct Point 

   public int x;
   public int y;
   

[StructLayout(LayoutKind.Explicit)]
public struct Rect 

   [FieldOffset(0)] public int left;
   [FieldOffset(4)] public int top;
   [FieldOffset(8)] public int right;
   [FieldOffset(12)] public int bottom;
   

(来自http://msdn.microsoft.com/en-us/library/system.runtime.interopservices.layoutkind.aspx)

【讨论】:

以上是关于从 p/invoke 修改结构布局的主要内容,如果未能解决你的问题,请参考以下文章

CPU 体系结构独立 P/Invoke:DllName 或路径可以是“动态的”吗?

P/Invoke 编组和解组 C# 和非托管 DLL 之间的二维数组、结构和指针

p/invoke 方法返回空结构

P Invoke struct结构

P/Invoke疑难杂症

在通过 P/Invoke 获得的 C++ 结构上设置 C# 回调