读取备用流结果为NullReferenceException

时间:2014-09-26 15:19:37

标签: c# .net batch-file unmanaged unsafe

我想要实现的最终任务是从我从互联网上下载的批处理文件中读取区域信息。我的实施基于此SO answer以及MSDN documentation of UnmanagedMemoryStream等其他一些资源。下面你可以看到我能想到的内容,但是,代码只会在NullReferenceException上抛出ReadByte而我完全不知道为什么。想法?

private static void Main(string[] args)
{
    var mainStream = NativeMethods.CreateFileW(
    "<path to batch file directory>any.bat:Zone.Identifier",
    NativeConstants.GENERIC_READ,
    NativeConstants.FILE_SHARE_READ,
    IntPtr.Zero,
    NativeConstants.OPEN_EXISTING,
    0,
    IntPtr.Zero);
    unsafe
    {
        using (var memoryStream = new UnmanagedMemoryStream((byte*)mainStream.ToPointer(), 1, 1, FileAccess.Read))
        {
            var zoneInfo = memoryStream.ReadByte();
        }
    }
}

public partial class NativeMethods
{
    /// Return Type: HANDLE->void*
    ///lpFileName: LPCWSTR->WCHAR*
    ///dwDesiredAccess: DWORD->unsigned int
    ///dwShareMode: DWORD->unsigned int
    ///lpSecurityAttributes: LPSECURITY_ATTRIBUTES->_SECURITY_ATTRIBUTES*
    ///dwCreationDisposition: DWORD->unsigned int
    ///dwFlagsAndAttributes: DWORD->unsigned int
    ///hTemplateFile: HANDLE->void*
    [CLSCompliantAttribute(false)]
    [DllImport("kernel32.dll", EntryPoint = "CreateFileW")]
    public static extern System.IntPtr CreateFileW(
        [InAttribute()] [MarshalAsAttribute(UnmanagedType.LPWStr)] string lpFileName,
        uint dwDesiredAccess,
        uint dwShareMode,
        [InAttribute()] System.IntPtr lpSecurityAttributes,
        uint dwCreationDisposition,
        uint dwFlagsAndAttributes,
        [InAttribute()] System.IntPtr hTemplateFile
    );
}

[CLSCompliantAttribute(false)]
public partial class NativeConstants
{
    /// GENERIC_WRITE -> (0x40000000L)
    public const uint GENERIC_WRITE = 1073741824;

    /// GENERIC_READ -> (0x80000000L)
    public const uint GENERIC_READ = 2147483648;

    /// FILE_SHARE_READ -> 0x00000001
    public const uint FILE_SHARE_READ = 1;

    /// OPEN_EXISTING -> 3
    public const uint OPEN_EXISTING = 3;
}

一些注意事项:

  • mainStream值有效,不是-1
  • memoryStream行为就像一切都很好,canRead返回 true等。

要重现此问题,即为了获得一些测试批处理文件,您可以创建文件“any.bat”,将其上传到例如谷歌驱动并下载它。这样就应该将区域信息添加到此文件中,并且最重要的是Zone.Identifier中的值应设置为3,这将保留ZONE_INTERNET

堆栈跟踪没有透露任何有趣的恕我直言:

  

at System.IO.UnmanagedMemoryStream.ReadByte()at   ConsoleApplication1.Program.Main(String [] args)in   c:\ Users \ MH185162 \ Documents \ Visual Studio   2012 \项目\ ConsoleApplication1 \ ConsoleApplication1 \的Program.cs:行   41在System.AppDomain._nExecuteAssembly(RuntimeAssembly程序集,   System.AppDomain.ExecuteAssembly上的String [] args)(String   assemblyFile,Evidence assemblySecurity,String [] args)at   Microsoft.VisualStudio.HostingProcess.HostProc.RunUsersAssembly()
  在System.Threading.ThreadHelper.ThreadStart_Context(对象状态)
  在System.Threading.ExecutionContext.RunInternal(ExecutionContext   executionContext,ContextCallback回调,对象状态,布尔值   preserveSyncCtx)at   System.Threading.ExecutionContext.Run(执行上下文   executionContext,ContextCallback回调,对象状态,布尔值   preserveSyncCtx)at   System.Threading.ExecutionContext.Run(执行上下文   executionContext,ContextCallback回调,对象状态)at   System.Threading.ThreadHelper.ThreadStart()

1 个答案:

答案 0 :(得分:1)

CreateFileW不返回指向某个内存位置的指针,它返回句柄。要使用此功能,您需要FileStream,而不是UnmanagedMemoryStream

using (var stream = new FileStream(mainStream, FileAccess.Read))

虽然构造函数的这个重载已经过时,但您应该使用SafeFileHandle而不是IntPtr(并确保Dispose它:)

[CLSCompliant(false)]
[DllImport("kernel32.dll", EntryPoint = "CreateFileW")]
public static extern SafeFileHandle CreateFileW(
    [In] [MarshalAs(UnmanagedType.LPWStr)] string lpFileName,
    uint dwDesiredAccess,
    uint dwShareMode,
    [In] IntPtr lpSecurityAttributes,
    uint dwCreationDisposition,
    uint dwFlagsAndAttributes,
    [In] IntPtr hTemplateFile
);

另外,我对Zone.Identifier一无所知,但对我来说,流不包含单个字符,就像你似乎期待的那样,它包含:

[ZoneTransfer]
ZoneId=3

要实现这一点,您可以将FileStream打包在StreamReader中。因此,获取上述字符串的整个代码是:

using (var streamHandle = NativeMethods.CreateFileW(
    @"<path to batch file directory>any.bat:Zone.Identifier",
    NativeConstants.GENERIC_READ, NativeConstants.FILE_SHARE_READ,
    IntPtr.Zero, NativeConstants.OPEN_EXISTING, 0, IntPtr.Zero))
using (var stream = new FileStream(streamHandle, FileAccess.Read))
using (var reader = new StreamReader(stream))
{
    Console.WriteLine(reader.ReadToEnd());
}