C# FindNextFile错误\u无效\u名称

C# FindNextFile错误\u无效\u名称,c#,winapi,pinvoke,C#,Winapi,Pinvoke,我正在尝试使用WINAPI函数FindFirstFile和FindNextFile。 但是,我遇到了一些问题 当我第一次调用函数FindFirstFile时,它工作正常。我有一个有效的处理程序,第一个文件夹/文件名在WIN32_FIND_数据结构中正确填充。找不到GetLastError的错误 然后,我调用FindNextFile,它返回true,因为我正在扫描的目录中有更多文件夹。但是我无法检索下一个文件夹/文件名,GetLastError返回123(0x7B)ERROR\u INVALID\

我正在尝试使用WINAPI函数FindFirstFileFindNextFile。 但是,我遇到了一些问题

当我第一次调用函数FindFirstFile时,它工作正常。我有一个有效的处理程序,第一个文件夹/文件名在WIN32_FIND_数据结构中正确填充。找不到GetLastError的错误

然后,我调用FindNextFile,它返回true,因为我正在扫描的目录中有更多文件夹。但是我无法检索下一个文件夹/文件名,GetLastError返回123(0x7B)ERROR\u INVALID\u name。 我有点困惑,正如官方文档中所说的,如果出现错误,它应该返回0

返回值

如果函数成功,则返回值为非零,lpFindFileData参数包含有关找到的下一个文件或目录的信息。 如果函数失败,则返回值为零,lpFindFileData的内容不确定。要获取扩展的错误信息,请调用GetLastError函数。 如果由于找不到更多匹配文件而导致函数失败,则GetLastError函数将返回ERROR\u no\u more\u文件

我正在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;
}

由于某些原因,moreFiles始终为true,GetLastError返回错误\u无效\u名称

如果你需要任何细节,请问我。
任何帮助都将不胜感激

只有调用
封送。GetLastWin32Error
是API调用报告失败。在
FindNextFile
的情况下,它通过返回
false
来执行此操作。您正在不加区别地检查由
封送处理.GetLastWin32Error返回的值

当文档告诉您函数如何指示故障时,它就清楚地说明了这一点。你甚至链接了文本。但是你说:

我有点困惑,因为它在官方文档中说,如果发生错误,它应该返回0

对。因此,根据
0
检查返回值。如果一个
BOOL
被封送为C#
BOOL
,这意味着如果函数失败,它将返回
false
。但是您只是忽略了返回值并测试了由
Marshal.GetLastWin32Error()
返回的值,这是完全不同的

代码应该更像这样:

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);
入口点不正确。因此,您实际上调用的是
FindFirstFile
,而不是
FindNextFile
,失败也就不足为奇了

在不需要时指定
入口点
,只会自找麻烦。而你正好掉进了陷阱。我要这样申报这些进口产品:

[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
{
    ....
}

最后,在我看来,所有这些代码都是毫无意义的。
目录.EnumerateFiles
目录.EnumerateDirectory
有什么问题?

只有调用
封送。GetLastWin32Error
是API调用报告失败。在
FindNextFile
的情况下,它通过返回
false
来执行此操作。您正在不加区别地检查由
封送处理.GetLastWin32Error返回的值

当文档告诉您函数如何指示故障时,它就清楚地说明了这一点。你甚至链接了文本。但是你说:

我有点困惑,因为它在官方文档中说,如果发生错误,它应该返回0

对。因此,根据
0
检查返回值。如果一个
BOOL
被封送为C#
BOOL
,这意味着如果函数失败,它将返回
false
。但是您只是忽略了返回值并测试了由
Marshal.GetLastWin32Error()
返回的值,这是完全不同的

代码应该更像这样:

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);
入口点不正确。因此,您实际上调用的是
FindFirstFile
,而不是
FindNextFile
,失败也就不足为奇了

在不需要时指定
入口点
,只会自找麻烦。而你正好掉进了陷阱。我要这样申报这些进口产品:

[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
{
    ....
}

最后,在我看来,所有这些代码都是毫无意义的。
目录.EnumerateFiles
目录.EnumerateDirectory
有什么问题?

不要调用
GetLastError()
,除非某些东西实际发生故障(即返回记录的故障代码),否则结果将毫无意义。重新发明轮子时,确保你想出的东西确实像一个轮子。如果这太乏味了,就用或代替。完整的功能记录在中。除非某些东西实际出现故障(即返回记录的故障代码),否则不要调用
GetLastError()
,否则结果将毫无意义。在重新发明轮子时,请确保您所提出的确实与轮子相似。如果这太乏味了,就用或代替。完整的功能记录在。1)这可能是一个意见问题,您可以随意不同意,但当我定义p/Invoke signa时