.NET Interop IntPtr与ref

时间:2009-12-15 22:32:55

标签: c# .net winapi interop intptr

可能是一个菜鸟问题但是互操作不是我的优点之一。

除了限制重载次数之外,还有任何理由我应该声明我的DllImports:

[DllImport("user32.dll")]
public static extern int SendMessage(IntPtr hWnd, int msg, int wParam, IntPtr lParam);

并像这样使用它们:

IntPtr lParam = Marshal.AllocCoTaskMem(Marshal.SizeOf(formatrange));
Marshal.StructureToPtr(formatrange, lParam, false);

int returnValue = User32.SendMessage(_RichTextBox.Handle, ApiConstants.EM_FORMATRANGE, wParam, lParam);

Marshal.FreeCoTaskMem(lParam);

而不是创建目标重载:

[DllImport("user32.dll")]
public static extern int SendMessage(IntPtr hWnd, int msg, int wParam, ref FORMATRANGE lParam);

使用它像:

FORMATRANGE lParam = new FORMATRANGE();
int returnValue = User32.SendMessage(_RichTextBox.Handle, ApiConstants.EM_FORMATRANGE, wParam, ref lParam);

by ref重载最终会更容易使用,但我想知道是否存在我不知道的缺点。

修改

到目前为止,有很多很棒的信息。

@P爸爸:你有一个基于抽象(或任何)类的结构类的例子吗?我将签名改为:

[DllImport("user32.dll", SetLastError = true)]
public static extern int SendMessage(IntPtr hWnd, int msg, int wParam, [In, Out, MarshalAs(UnmanagedType.LPStruct)] CHARFORMAT2 lParam);

没有InOutMarshalAs SendMessage(我的测试中的EM_GETCHARFORMAT)失败。以上示例效果很好,但如果我将其更改为:

[DllImport("user32.dll", SetLastError = true)]
public static extern int SendMessage(IntPtr hWnd, int msg, int wParam, [In, Out, MarshalAs(UnmanagedType.LPStruct)] NativeStruct lParam);

我收到一个System.TypeLoadException,指出CHARFORMAT2格式无效(我会尝试在这里捕获它)。

例外:

无法从程序集“CC.Utilities,Version = 1.0.9.1212,Culture = neutral,PublicKeyToken = 111aac7a42f7965e”加载类型“CC.Utilities.WindowsApi.CHARFORMAT2”,因为格式无效。

NativeStruct类:

public class NativeStruct
{
}

我已尝试abstract,添加了StructLayout属性等,我得到了同样的例外。

[StructLayout(LayoutKind.Sequential)]
public class CHARFORMAT2: NativeStruct
{
    ...
}

修改

我没有按照常见问题解答,我问了一个可以讨论但没有得到积极回答的问题。除此之外,这个帖子中还有很多有见地的信息。所以我会把它留给读者投票给答案。第一个到10个以上的投票将是答案。如果在两天(太平洋标准时间12/17)没有答案符合这一点,我将添加我自己的答案,总结线程中的所有美味知识: - )

再次编辑:

我撒了谎,接受了P爸爸的回答,因为他是那个男人并得到了很大的帮助(他也有一只可爱的小猴子:-P)

5 个答案:

答案 0 :(得分:15)

答案 1 :(得分:3)

我有一些有趣的案例,参数类似于ref Guid parent,相应的文档说:

  

“指向指定父级的GUID的指针。传递空指针以使用 [插入一些系统定义的项目] 。”

如果null(或IntPtr.Zero参数IntPtr)确实是无效参数,那么您可以使用ref参数 - 可能更好,因为它是更清楚你需要通过什么。

如果null是有效参数,则可以传递ClassType而不是ref StructType。引用类型(class)的对象作为指针传递,它们允许null

答案 2 :(得分:2)

不,你不能重载SendMessage并使wparam参数成为int。这将使您的程序在64位版本的操作系统上失败。它必须是一个指针,IntPtr,blittable引用或out或ref值类型。重载out / ref类型也不错。


编辑:正如OP指出的那样,这实际上不是问题。 64位函数调用约定通过寄存器传递前4个参数,而不是堆栈。因此,对于wparam和lparam参数,没有堆栈错位的危险。

答案 3 :(得分:1)

我没有看到任何缺点。

对于简单类型和简单结构,副词通常就足够了。

如果结构具有可变大小或者您想要进行自定义处理,则应该优先使用IntPtr。

答案 4 :(得分:1)

使用ref比手动操作指针更简单且更不容易出错,因此我认为没有充分理由不使用它...使用ref的另一个好处是你没有担心释放非托管分配的内存