.NET Interop IntPtr与ref

laut 发布于 2019-11-10 .net 最后更新 2019-11-10 12:11 84 浏览

可能是一个noob问题,但interop不是我的优点之一。 除了限制重载次数之外,还有什么原因需要声明我的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);
通过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 PST),我会添加自己的答案,总结线程中的所有美味知识:-) 再次编辑: 我撒谎,接受P爸爸的回答,因为他是男人,并且是一个很好的帮助(他也有一只可爱的小猴子:-P)
已邀请:

jdolor

赞同来自:

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


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

vvel

赞同来自:

内容太长未翻译

zvelit

赞同来自:

我没有看到任何缺点。 对于简单类型和简单结构,By-ref通常就足够了。 如果结构具有可变大小或者您想要进行自定义处理,则应该优先使用IntPtr。

tiste

赞同来自:

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

"Pointer to a GUID specifying the parent. Pass a null pointer to use [insert some system-defined item]."
如果null(或IntPtr参数的IntPtr.Zero)确实是一个无效参数,那么你可以使用ref参数 - 可能更好,因为它更清楚你需要通过什么。 如果null是有效参数,则可以传递ClassType而不是ref StructType。引用类型的对象(class)作为指针传递,它们允许null

comnis

赞同来自:

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