我正在尝试在 powershell 中实现一个函数,它可以可靠地告诉我两个有效路径名引用的目录是否确实相同。它需要能够处理由映射、UNC 路径、连接点、可能由文件服务器上的连接点等引用的目录。等等。
鉴于引用目录的方法多种多样,似乎最可靠的方法是使用一个目录路径名将一个空文件写入目录,然后使用第二个路径名测试是否可以在第二个路径名下找到临时文件。然而,事实证明这可能并不可靠。
我们有一个文件服务器,其目录S:\
以 的形式共享\\server\share
。服务器上有目录S:\rootDir1
、 S:\rootDir1\subDir
、 S:\rootDir2
和一个连接点 S:\rootDir2\subDir => S:\rootDir1\subDir
。
在应用服务器上,我映射M:\ => \\server\share
,因此应用服务器可以看到M:\rootDir1\subDir
并M:\rootDir2\subDir
在应用服务器上的 powershell 中,我M:\rootDir1\subDir
通过创建一个文件
New-Item -ItemType File -Path `M:\rootDir1\subDir\testFile.txt`
然后我们立即执行两个测试
$l_ret1 = test-path `M:\rootDir1\subDir\testFile.txt`
$l_ret2 = test-path `M:\rootDir2\subDir\testFile.txt`
返回的结果$l_ret1
始终是$true
,而结果$l_ret2
不一致,在创建测试文件和测试之间没有明显的时间延迟。 有没有办法强制 Windows “更新”或“刷新” 中有关文件的信息M:\rootDir2\subDir
? 我意识到这可能是文件服务器(Windows 文件服务器)的错误,但问题就变成了,有没有办法强制文件服务器更新其信息?
答案1
要确定它们是否是同一目录,请尝试使用:
通过句柄获取文件信息 https://msdn.microsoft.com/en-us/library/aa364952(v=vs.85).aspx
那里的备注部分说“...您可以比较 BY_HANDLE_FILE_INFORMATION 结构中返回的 VolumeSerialNumber 和 FileIndex 成员来确定两个路径是否映射到同一个目标;例如,您可以比较两个文件路径并确定它们是否映射到同一个目录......”
答案2
扩展 Warren 的回答:可以在 Powershell 中使用以下 powershell 代码:
function Global:LoadCode()
{
Add-Type -MemberDefinition @"
[StructLayout(LayoutKind.Sequential)]
public struct BY_HANDLE_FILE_INFORMATION
{
public uint FileAttributes;
public System.Runtime.InteropServices.ComTypes.FILETIME CreationTime;
public System.Runtime.InteropServices.ComTypes.FILETIME LastAccessTime;
public System.Runtime.InteropServices.ComTypes.FILETIME LastWriteTime;
public uint VolumeSerialNumber;
public uint FileSizeHigh;
public uint FileSizeLow;
public uint NumberOfLinks;
public uint FileIndexHigh;
public uint FileIndexLow;
};
[DllImport("kernel32.dll", SetLastError = true)]
private static extern bool GetFileInformationByHandle(IntPtr hFile, out BY_HANDLE_FILE_INFORMATION lpFileInformation);
[DllImport("kernel32.dll", EntryPoint = "CreateFileW", CharSet = CharSet.Unicode, SetLastError = true)]
public static extern SafeFileHandle CreateFile(string lpFileName, int dwDesiredAccess, int dwShareMode,
IntPtr SecurityAttributes, int dwCreationDisposition, int dwFlagsAndAttributes, IntPtr hTemplateFile);
private static SafeFileHandle MY_GetFileHandle(string dirName)
{
const int FILE_ACCESS_NEITHER = 0;
const int FILE_SHARE_READ = 1;
const int FILE_SHARE_WRITE = 2;
const int CREATION_DISPOSITION_OPEN_EXISTING = 3;
const int FILE_FLAG_BACKUP_SEMANTICS = 0x02000000;
return CreateFile(dirName, FILE_ACCESS_NEITHER, (FILE_SHARE_READ | FILE_SHARE_WRITE), System.IntPtr.Zero, CREATION_DISPOSITION_OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, System.IntPtr.Zero);
}
private static BY_HANDLE_FILE_INFORMATION? MY_GetFileInfo(SafeFileHandle directoryHandle)
{
BY_HANDLE_FILE_INFORMATION objectFileInfo;
if ((directoryHandle == null) || (!GetFileInformationByHandle(directoryHandle.DangerousGetHandle(), out objectFileInfo)))
{
return null;
}
return objectFileInfo;
}
public static bool MY_AreDirsEqual(string dirName1, string dirName2)
{ //
bool bRet = false;
//NOTE: we cannot lift the call to GetFileHandle into GetFileInfo, because we _must_
// have both file handles open simultaneously in order for the objectFileInfo comparison
// to be guaranteed as valid.
using (SafeFileHandle directoryHandle1 = MY_GetFileHandle(dirName1), directoryHandle2 = MY_GetFileHandle(dirName2))
{
BY_HANDLE_FILE_INFORMATION? objectFileInfo1 = MY_GetFileInfo(directoryHandle1);
BY_HANDLE_FILE_INFORMATION? objectFileInfo2 = MY_GetFileInfo(directoryHandle2);
bRet = objectFileInfo1 != null
&& objectFileInfo2 != null
&& (objectFileInfo1.Value.FileIndexHigh == objectFileInfo2.Value.FileIndexHigh)
&& (objectFileInfo1.Value.FileIndexLow == objectFileInfo2.Value.FileIndexLow)
&& (objectFileInfo1.Value.VolumeSerialNumber == objectFileInfo2.Value.VolumeSerialNumber);
}
return bRet;
}
"@ -Name Win32 -NameSpace System -UsingNamespace System.Text,Microsoft.Win32.SafeHandles,System.ComponentModel
}
function Global:Get_AreDirsEqual([string]$p_source, [string]$p_target)
{ Mcc
if( ( ([System.Management.Automation.PSTypeName]'System.Win32').Type -eq $null) -or ([system.win32].getmethod('MY_AreDirsEqual') -eq $null) )
{
LoadCode
}
[System.Win32]::MY_AreDirsEqual($p_source, $p_target)
}
请注意,这实际上实现了 c# 中的功能。