如何在SetFilePointer中将ref int设置为null(file.SafeFileHandle,moveDistance,ref moveDistanceHighBits,EMoveMethod.Begin)

时间:2015-02-12 15:51:38

标签: c# .net-4.0

我尝试了在Using SetFilePointer in C# has unblanced the stack中提出的解决方案来移动文件的指针并且它有效。唯一的问题是,当读取设备而不是文件时,setfilepointer将指针放在包含所需地址的扇区的开头,而不是将其放在所需的地址本身。不知道为什么。 但我的问题是另一个问题。根据我已经阅读的文档,如果您不需要使用moveDistanceHighBits,因为只需要低位字节来解决您想要的偏移量,您必须将moveDistanteHighBits设置为null。但我不知道该怎么做。 有人可以帮忙吗?

2 个答案:

答案 0 :(得分:2)

这不仅仅是因为winapi函数的特殊情况。与25年前使用的C编译器不同,C#很好地支持64位整数。而且风险太大了。您想要编写一个包装器方法,以便在winapi函数失败时正确抛出异常。永远不要跳过它。这样做:

using System;
using System.IO;
using System.Runtime.InteropServices;
using Microsoft.Win32.SafeHandles;

    public class NativeMethods {
        public static void SetFilePointer(SafeFileHandle hFile, long dist, SeekOrigin method) {
            int lodist = (int)dist;
            int hidist = (int)(dist >> 32);
            int retval = SetFilePointer(hFile, lodist, ref hidist, method);
            if (retval == -1) throw new System.ComponentModel.Win32Exception();
        }

        [DllImport("Kernel32.dll", SetLastError = true)]
        private static extern int SetFilePointer(SafeFileHandle hFile, 
            int distlo, ref int disthi, SeekOrigin method);

    }
}

请注意,您根本不需要根据它进行调整。您想要使用其中一个采用IntPtr或SafeHandle的FileStream构造函数。例如This one。现在您只需使用FileStream.Seek()。

答案 1 :(得分:1)

您可以更改PInvoke签名以使用IntPtr,然后在您想要传递IntPtr.Zero时传递NULL。新签名看起来像这样:

[DllImport("kernel32.dll", EntryPoint="SetFilePointer", SetLastError=true)]
static extern uint SetFilePointer(
    [In] Microsoft.Win32.SafeHandles.SafeFileHandle hFile, 
    [In] int lDistanceToMove, 
    [In] IntPtr lpDistanceToMoveHigh, 
    [In] EMoveMethod dwMoveMethod);

这是有效的,因为在两个定义中,您传递指针但使用IntPtr可以控制实际的指针值。当您使用ref时,您必须拥有一个现有的局部变量,编译器将获取其地址。

如果您需要从NULL来电回复/接收值(SetFilePointer除外),您可以这样做:

int nValue = ...; // Value to pass for lpDistanceToMoveHigh
IntPtr pInt = IntPtr.Zero;

try
{           
    pInt = Marshal.AllocHGlobal ( sizeof ( int ) );
    Marshal.WriteInt32 ( pInt, nValue );

    // Call SetFilePointer(); upon successful return,
    // you can read the value returned for lpDistanceToMoveHigh
    nValue = Marshal.ReadInt32 ( pInt );

    // ...
}
finally
{
    if ( pInt != IntPtr.Zero )
        Marshal.FreeHGlobal ( pInt );
}