托管代码中的指针? C#

时间:2010-07-16 10:36:07

标签: c#

我使用C#.net。

现在这是我的方法:

    [DllImport(DLLPath, CallingConvention = CallingConvention.Cdecl)]
    unsafe public extern static int AMRecoveryModeDeviceReboot(AMRecoveryDevice device, byte[] paramByte, int u1, int u2, int u3)

我必须有一个指针,AMRecoveryDevice是一个结构:

[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi, Pack = 1)]
internal struct AMRecoveryDevice
{
    [MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)]
    public byte[] unknown0;      /* 0 */
    public DeviceRestoreNotificationCallback callback;    /* 8 */
    public IntPtr user_info;      /* 12 */
    [MarshalAs(UnmanagedType.ByValArray, SizeConst = 12)]
    public byte[] unknown1;      /* 16 */
    public uint readwrite_pipe;    /* 28 */
    public byte read_pipe;          /* 32 */
    public byte write_ctrl_pipe;    /* 33 */
    public byte read_unknown_pipe;  /* 34 */
    public byte write_file_pipe;    /* 35 */
    public byte write_input_pipe;   /* 36 */
};

现在我必须在第一种方法中将AMRecoveryDevice作为指针,但它会产生错误。 这不可能吗?

5 个答案:

答案 0 :(得分:9)

在方法声明中使用ref:

[DllImport(DLLPath, CallingConvention = CallingConvention.Cdecl)]
public extern static int AMRecoveryModeDeviceReboot(
    ref AMRecoveryDevice device,
    byte[] paramByte,
    int u1,
    int u2,
    int u3)

答案 1 :(得分:4)

device设为ref参数:

[DllImport(DLLPath, CallingConvention = CallingConvention.Cdecl)]
unsafe public extern static int AMRecoveryModeDeviceReboot(
    ref AMRecoveryDevice device, 
    byte[] paramByte, 
    int u1, 
    int u2, 
    int u3)

关于如何将数据传递给P / Invoke调用的好文章来自MSDN杂志:

  

<强> Jason Clark: P/Invoke Revisited

答案 2 :(得分:2)

将其他所有内容保持不变,您只需将struct更改为class

由于您已指定顺序布局,因此它的行为就像指向结构的指针。

IOW:

[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi, Pack = 1)]
internal class AMRecoveryDevice
{
  ...
}    

...

[DllImport(DLLPath, CallingConvention = CallingConvention.Cdecl)]
extern static int AMRecoveryModeDeviceReboot(AMRecoveryDevice device, 
   byte[] paramByte, int u1, int u2, int u3)

答案 3 :(得分:1)

我通常使用的模式是将P / invoke声明设为私有,并使用IntPtr代替结构。提供处理编组的公共方法。 (你也可以通过这种方式摆脱不安全的情况。)

[DllImport(DLLPath, CallingConvention = CallingConvention.Cdecl)]
private extern static int AMRecoveryModeDeviceReboot(IntPtr device, byte[] paramByte, int u1, int u2, int u3)

public static int AMRecoveryModeDevice(ref AMRecoveryDevice device, byte[] paramByte, int u1, int u2, int u3) {
    var ptr = Marshal.AllocHGlobal(Marshal.SizeOf(device));
    Marshal.StructureToPointer(device, ptr, false);
    int result = AMRecoveryModeDeviceReboot(ptr, paramByte, u1, u2, u3);
    device = (AMRecoveryDevice)Marshal.PtrToStructure(ptr, typeof(AMRecoveryDevice));
    Marshal.FreeHGlobal(ptr);
    return result;
}

对于您的AMRecoveryDevice结构,您也应该使用IntPtr作为回调委托。

[MarshalAs(UnmanagedType.FunctionPtr)]
private IntPtr _callback;    /* 8 */
public DeviceRestoreNotificationCallback callback {
    get { return (DeviceRestoreNotificationCallback)Marsal.GetDelagateFromFunctionPointer(_callback, typeof(DeviceRestoreNotificationCallback)); }
    set { _calback = Marshal.GetFunctionPointerFromDelegate(value); }
}

答案 4 :(得分:0)

如果您在不安全的环境中工作,则不会。请参阅:http://msdn.microsoft.com/en-us/library/chfa2zb8(VS.71).aspx

虽然不建议在托管应用中使用。