编辑:这个问题最初是关于C ++和C#中的代码之间的行为差异,但事实证明这是一个红色的鲱鱼。问题已被重写,以更好地反映我遇到的问题。
我尝试使用NtQueryInformationFile函数列出SMB服务器上托管的文件的备用流。到目前为止,我编写的代码可以正常使用大多数服务器,但有时在非samba Mac OS X SMB服务器上运行时会失败。
我已将应用程序的代码缩减为可靠地再现问题的以下代码:
using System;
using System.IO;
using System.Runtime.InteropServices;
using Microsoft.Win32.SafeHandles;
namespace Streams
{
class Program
{
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
public class NETRESOURCE
{
public uint dwScope;
public uint dwType;
public uint dwDisplayType;
public uint dwUsage;
public string lpLocalName;
public string lpRemoteName;
public string lpComment;
public string lpProvider;
}
[DllImport("ntdll.dll", SetLastError = false, CharSet = CharSet.Unicode)]
public static extern uint NtQueryInformationFile(
SafeFileHandle handle,
IntPtr IoStatusBlock,
IntPtr pInfoBlock,
uint length,
uint fileInformationClass);
[DllImport("kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
public static extern SafeFileHandle CreateFile(
string fileName,
uint desiredAccessMask,
uint shareMode,
IntPtr lpSecurityAttributes,
uint creationDisposition,
uint flagsAndAttributes,
IntPtr hTemplateFile);
[DllImport("mpr.dll", SetLastError = true, CharSet = CharSet.Unicode)]
public static extern uint WNetAddConnection2(NETRESOURCE netResource, [MarshalAs(UnmanagedType.LPTStr)] string password, [MarshalAs(UnmanagedType.LPTStr)] string username, uint flags);
static void Main(string[] args)
{
var server = args[0];
var username = args[1];
var password = args[2];
var uncPath = args[3];
NETRESOURCE netResource = new NETRESOURCE();
netResource.dwType = 0x1 /* DISK */;
netResource.lpRemoteName = server;
WNetAddConnection2(netResource, password, username, 0x00000004 /* CONNECT_TEMPORARY */);
var fh = CreateFile(
uncPath,
0x80000000 /* GENERIC_READ */,
(uint)(FileShare.Read | FileShare.Write | FileShare.Delete),
IntPtr.Zero,
3 /* OPEN_EXISTING */,
0x08000000 /* SequentialScan */ | 0x02000000 /* BackupSemantics */,
IntPtr.Zero
);
IntPtr status = Marshal.AllocHGlobal(1024);
uint bufferSize = 64*1024;
IntPtr buffer = Marshal.AllocHGlobal((int)bufferSize);
uint ntStatus = NtQueryInformationFile(fh, status, buffer, bufferSize, 22 /* FileStreamInformation */);
Console.WriteLine($"NtQueryFileInformation returned {ntStatus}");
Console.ReadKey();
}
}
}
在大多数情况下,ntStatus的值是STATUS_OK,如预期的那样。当我尝试在具有Mac OS X SMB服务器的备用数据流的文件上使用它时,我得到STATUS_INVALID_NETWORK_RESPONSE。该错误代码似乎表明来自服务器的响应不正确,但是当我使用Wireshark进行检查时,与返回STATUS_OK时相比没有差异。
为了使事情变得更奇怪,当我以32位模式运行它时,此代码有效(在Visual Studio项目设置中设置'首选32位')。在64位模式下运行时,我总是得到错误代码。 那么明显的错误就是所讨论的代码不是64位干净,而是AFAICT上面的代码。
我也尝试用GetFileInformationByHandleEx调用替换NtQueryInformationFile调用,但这给了我相同的结果(当然使用Win32错误代码而不是NT状态代码)。
什么可以解释32位和64位模式之间的行为差异?