查找下一个文件 ERROR_INVALID_NAME
FindNextFile ERROR_INVALID_NAME
我正在尝试使用 WINAPI 函数 FindFirstFile 和 FindNextFile。
但是,我遇到了一些问题。
当我第一次调用 FindFirstFile 函数时,它工作正常。我有一个有效的处理程序,第一个 folder/file 名称已正确填充到 WIN32_FIND_DATA 结构中。 GetLastError 未发现错误。
然后,我调用 FindNextFile return 是真的,因为我正在扫描的目录中有更多文件夹。但是我无法检索下一个 folder/file 名称和 GetLastError returns 123 (0x7B) ERROR_INVALID_NAME。
我有点困惑,因为它在官方文档中说,如果发生错误,它应该 return 0.
https://msdn.microsoft.com/en-us/library/windows/desktop/aa364428(v=vs.85).aspx
Return value
If the function succeeds, the return value is nonzero and the lpFindFileData parameter contains information about the next file or directory found.
If the function fails, the return value is zero and the contents of lpFindFileData are indeterminate. To get extended error information, call the GetLastError function.
If the function fails because no more matching files can be found, the GetLastError function returns ERROR_NO_MORE_FILES.
我在 Windows 7 x64 上使用 .NET 4.5.1 和 Visual Studio 2013。
这是示例代码。
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public struct WIN32_FIND_DATA
{
public uint dwFileAttributes;
public System.Runtime.InteropServices.ComTypes.FILETIME ftCreationTime;
public System.Runtime.InteropServices.ComTypes.FILETIME ftLastAccessTime;
public System.Runtime.InteropServices.ComTypes.FILETIME ftLastWriteTime;
public uint nFileSizeHigh;
public uint nFileSizeLow;
public uint dwReserved0;
public uint dwReserved1;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 260)]
public string cFileName;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 14)]
public string cAlternateFileName;
}
...
[DllImport("Kernel32.dll", EntryPoint = "FindFirstFile", SetLastError = true)]
public static extern IntPtr FindFirstFile(string lpFileName, ref WIN32_FIND_DATA lpFindFileData);
[return: MarshalAs(UnmanagedType.Bool)]
[DllImport("Kernel32.dll", EntryPoint = "FindFirstFile", SetLastError = true)]
public static extern bool FindNextFile(IntPtr hFindFile, ref WIN32_FIND_DATA lpFindFileData);
[return: MarshalAs(UnmanagedType.Bool)]
[DllImport("Kernel32.dll", EntryPoint = "FindClose", SetLastError = true)]
public static extern bool FindClose(IntPtr hFindFile);
...
public static void Test()
{
WIN32_FIND_DATA metaDataFile = new WIN32_FIND_DATA();
IntPtr nextHandle = FileScanner.FindFirstFile("C:\*", ref metaDataFile);
Console.WriteLine(Marshal.GetLastWin32Error()); // This equals 0x0 ERROR_SUCCESS
Console.WriteLine(metaDataFile.cFileName); // This equals $Recycle.Bin
/* Check invalid handler */
if (nextHandle != new IntPtr(-1L))
{
bool moreFiles = true;
while (moreFiles)
{
moreFiles = FileScanner.FindNextFile(nextHandle, ref metaDataFile);
Console.WriteLine(Marshal.GetLastWin32Error()); // This equals 0x7B ERROR_INVALID_NAME
Console.WriteLine(metaDataFile.cFileName); // This equals $Recycle.Bin and this value never change.
}
FindClose(nextHandle);
}
}
出于某种原因,moreFiles 始终为真,GetLastError returns ERROR_INVALID_NAME ...
如果您需要任何详细信息,请问我。
任何帮助将不胜感激!
只有调用Marshal.GetLastWin32Error
是API调用失败。在 FindNextFile
的情况下,它通过 returning false
来实现。您正在不加区别地检查由 Marshal.GetLastWin32Error
编辑的值 return。
文档在告诉您函数如何指示失败时清楚地说明了这一点。你甚至链接了文本。但是你说:
I'm a little confused as it says in the official documentation that if an error occurs it should return 0.
没错。因此,请对照 0
检查 return 值。在 BOOL
编组为 C# bool
的情况下,这意味着如果函数 returns false
失败。但是您只是忽略了 return 值并测试了由 Marshal.GetLastWin32Error()
编辑的值 return,完全不同。
代码应该更像这样:
public static void Test()
{
WIN32_FIND_DATA fd = new WIN32_FIND_DATA();
IntPtr findHandle = FileScanner.FindFirstFile("C:\*", ref fd);
if (findHandle == INVALID_HANDLE_VALUE)
throw new Win32Exception();
do
{
Console.WriteLine(fd.cFileName);
} while (FileScanner.FindNextFile(findHandle, ref fd));
// you might check that Marshal.GetLastWin32Error() returns ERROR_NO_MORE_FILES
// at this point, otherwise the enumeration failed abnormally
if (!FindClose(findHandle))
throw new Win32Exception();
}
您的另一个问题是您的 p/invoke 声明,这是对您伤害最大的问题。仔细看这个:
[return: MarshalAs(UnmanagedType.Bool)]
[DllImport("Kernel32.dll", EntryPoint = "FindFirstFile", SetLastError = true)]
public static extern bool FindNextFile(IntPtr hFindFile,
ref WIN32_FIND_DATA lpFindFileData);
EntryPoint
不正确。所以你实际上是在调用 FindFirstFile
而不是 FindNextFile
并且失败也就不足为奇了。
在不需要时指定 EntryPoint
简直是自找麻烦。而你已经掉进了陷阱。我会这样声明这些导入:
[DllImport("Kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
public static extern IntPtr FindFirstFile(string lpFileName,
ref WIN32_FIND_DATA lpFindFileData);
[DllImport("Kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
public static extern bool FindNextFile(IntPtr hFindFile,
ref WIN32_FIND_DATA lpFindFileData);
[DllImport("Kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
public static extern bool FindClose(IntPtr hFindFile);
请注意,不需要 return
属性,因为 UnmanagedType.Bool
是默认值。
然后您需要将结构上的 CharSet
更改为 CharSet.Unicode
以匹配。在这里选择 ANSI 毫无意义。
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
public struct WIN32_FIND_DATA
{
....
}
最后,所有这些代码在我看来都毫无意义。 Directory.EnumerateFiles
和 Directory.EnumerateDirectories
有什么问题?
我正在尝试使用 WINAPI 函数 FindFirstFile 和 FindNextFile。 但是,我遇到了一些问题。
当我第一次调用 FindFirstFile 函数时,它工作正常。我有一个有效的处理程序,第一个 folder/file 名称已正确填充到 WIN32_FIND_DATA 结构中。 GetLastError 未发现错误。
然后,我调用 FindNextFile return 是真的,因为我正在扫描的目录中有更多文件夹。但是我无法检索下一个 folder/file 名称和 GetLastError returns 123 (0x7B) ERROR_INVALID_NAME。 我有点困惑,因为它在官方文档中说,如果发生错误,它应该 return 0.
https://msdn.microsoft.com/en-us/library/windows/desktop/aa364428(v=vs.85).aspx
Return value
If the function succeeds, the return value is nonzero and the lpFindFileData parameter contains information about the next file or directory found. If the function fails, the return value is zero and the contents of lpFindFileData are indeterminate. To get extended error information, call the GetLastError function. If the function fails because no more matching files can be found, the GetLastError function returns ERROR_NO_MORE_FILES.
我在 Windows 7 x64 上使用 .NET 4.5.1 和 Visual Studio 2013。 这是示例代码。
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public struct WIN32_FIND_DATA
{
public uint dwFileAttributes;
public System.Runtime.InteropServices.ComTypes.FILETIME ftCreationTime;
public System.Runtime.InteropServices.ComTypes.FILETIME ftLastAccessTime;
public System.Runtime.InteropServices.ComTypes.FILETIME ftLastWriteTime;
public uint nFileSizeHigh;
public uint nFileSizeLow;
public uint dwReserved0;
public uint dwReserved1;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 260)]
public string cFileName;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 14)]
public string cAlternateFileName;
}
...
[DllImport("Kernel32.dll", EntryPoint = "FindFirstFile", SetLastError = true)]
public static extern IntPtr FindFirstFile(string lpFileName, ref WIN32_FIND_DATA lpFindFileData);
[return: MarshalAs(UnmanagedType.Bool)]
[DllImport("Kernel32.dll", EntryPoint = "FindFirstFile", SetLastError = true)]
public static extern bool FindNextFile(IntPtr hFindFile, ref WIN32_FIND_DATA lpFindFileData);
[return: MarshalAs(UnmanagedType.Bool)]
[DllImport("Kernel32.dll", EntryPoint = "FindClose", SetLastError = true)]
public static extern bool FindClose(IntPtr hFindFile);
...
public static void Test()
{
WIN32_FIND_DATA metaDataFile = new WIN32_FIND_DATA();
IntPtr nextHandle = FileScanner.FindFirstFile("C:\*", ref metaDataFile);
Console.WriteLine(Marshal.GetLastWin32Error()); // This equals 0x0 ERROR_SUCCESS
Console.WriteLine(metaDataFile.cFileName); // This equals $Recycle.Bin
/* Check invalid handler */
if (nextHandle != new IntPtr(-1L))
{
bool moreFiles = true;
while (moreFiles)
{
moreFiles = FileScanner.FindNextFile(nextHandle, ref metaDataFile);
Console.WriteLine(Marshal.GetLastWin32Error()); // This equals 0x7B ERROR_INVALID_NAME
Console.WriteLine(metaDataFile.cFileName); // This equals $Recycle.Bin and this value never change.
}
FindClose(nextHandle);
}
}
出于某种原因,moreFiles 始终为真,GetLastError returns ERROR_INVALID_NAME ...
如果您需要任何详细信息,请问我。 任何帮助将不胜感激!
只有调用Marshal.GetLastWin32Error
是API调用失败。在 FindNextFile
的情况下,它通过 returning false
来实现。您正在不加区别地检查由 Marshal.GetLastWin32Error
编辑的值 return。
文档在告诉您函数如何指示失败时清楚地说明了这一点。你甚至链接了文本。但是你说:
I'm a little confused as it says in the official documentation that if an error occurs it should return 0.
没错。因此,请对照 0
检查 return 值。在 BOOL
编组为 C# bool
的情况下,这意味着如果函数 returns false
失败。但是您只是忽略了 return 值并测试了由 Marshal.GetLastWin32Error()
编辑的值 return,完全不同。
代码应该更像这样:
public static void Test()
{
WIN32_FIND_DATA fd = new WIN32_FIND_DATA();
IntPtr findHandle = FileScanner.FindFirstFile("C:\*", ref fd);
if (findHandle == INVALID_HANDLE_VALUE)
throw new Win32Exception();
do
{
Console.WriteLine(fd.cFileName);
} while (FileScanner.FindNextFile(findHandle, ref fd));
// you might check that Marshal.GetLastWin32Error() returns ERROR_NO_MORE_FILES
// at this point, otherwise the enumeration failed abnormally
if (!FindClose(findHandle))
throw new Win32Exception();
}
您的另一个问题是您的 p/invoke 声明,这是对您伤害最大的问题。仔细看这个:
[return: MarshalAs(UnmanagedType.Bool)]
[DllImport("Kernel32.dll", EntryPoint = "FindFirstFile", SetLastError = true)]
public static extern bool FindNextFile(IntPtr hFindFile,
ref WIN32_FIND_DATA lpFindFileData);
EntryPoint
不正确。所以你实际上是在调用 FindFirstFile
而不是 FindNextFile
并且失败也就不足为奇了。
在不需要时指定 EntryPoint
简直是自找麻烦。而你已经掉进了陷阱。我会这样声明这些导入:
[DllImport("Kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
public static extern IntPtr FindFirstFile(string lpFileName,
ref WIN32_FIND_DATA lpFindFileData);
[DllImport("Kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
public static extern bool FindNextFile(IntPtr hFindFile,
ref WIN32_FIND_DATA lpFindFileData);
[DllImport("Kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
public static extern bool FindClose(IntPtr hFindFile);
请注意,不需要 return
属性,因为 UnmanagedType.Bool
是默认值。
然后您需要将结构上的 CharSet
更改为 CharSet.Unicode
以匹配。在这里选择 ANSI 毫无意义。
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
public struct WIN32_FIND_DATA
{
....
}
最后,所有这些代码在我看来都毫无意义。 Directory.EnumerateFiles
和 Directory.EnumerateDirectories
有什么问题?