我编写了一个获取目录树的函数。我目前正在使用 OS Api 来实现这一点。如果与 DLLImport 一起使用,一切都可以,但 Roslynator 是这样告诉我的:
“用‘LibraryImportAttribute’而不是‘DllImportAttribute’标记方法‘FindFirstFile’,以在编译时生成 P/Invoke 编组代码。”
我的代码:
public partial class WindowsDirectoryTreeTraversal : IDisposable
{
[DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
private static extern nint FindFirstFile(string lpFileName, out WIN32_FIND_DATA lpFindFileData);
[DllImport("kernel32.dll", SetLastError = true)]
private static extern bool FindNextFile(nint hFindFile, out WIN32_FIND_DATA lpFindFileData);
[DllImport("kernel32.dll", SetLastError = true)]
private static extern bool FindClose(nint hFindFile);
private bool disposed = false;
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing)
{
if (!disposed)
{
if (disposing)
{
// Dispose managed resources here, if any
}
// Release unmanaged resources (close opened directories, etc.)
// Note: Ensure you call closedir here for any remaining open directories
disposed = true;
}
}
private static bool IsArchive(WIN32_FIND_DATA findFileData)
{
return (findFileData.dwFileAttributes & FileAttributes.Archive) != 0;
}
public async Task<DirectoryNode> GetDirectoryTreeAsync(string path)
{
var directoryNode = new DirectoryNode(path, true);
var tasks = new List<Task>();
var stack = new Stack<(DirectoryNode node, string path)>();
stack.Push((directoryNode, path));
try
{
while (stack.Count > 0)
{
var (currentNode, currentPath) = stack.Pop();
tasks.Add(ProcessDirectoryAsync(currentNode, currentPath));
}
await Task.WhenAll(tasks);
}
catch (Exception e)
{
Console.WriteLine($"Error accessing {path}: {e.Message}");
}
return directoryNode;
}
private async Task ProcessDirectoryAsync(DirectoryNode currentNode, string currentPath)
{
IntPtr hFindFile = FindFirstFile(Path.Combine(currentPath, "*"), out WIN32_FIND_DATA findFileData);
if (hFindFile == IntPtr.Zero)
{
Console.WriteLine($"Error finding files in directory {currentPath}");
return;
}
try
{
do
{
string entryName = findFileData.cFileName;
if (entryName == "." || entryName == "..")
continue;
string entryPath = Path.Combine(currentPath, entryName);
if ((findFileData.dwFileAttributes & FileAttributes.Directory) != 0)
{
var subNode = new DirectoryNode(entryPath, true);
currentNode.Subitems.Add(subNode);
await ProcessDirectoryAsync(subNode, entryPath);
}
else if (IsArchive(findFileData) && ArchiveRegex().IsMatch(entryName[entryName.LastIndexOf(".")..]))
{
currentNode.Subitems.Add(new DirectoryNode(entryPath, false));
}
} while (FindNextFile(hFindFile, out findFileData));
}
catch (Exception e)
{
Console.WriteLine($"Error reading dir {currentPath}: {e.Message}");
}
finally
{
FindClose(hFindFile);
}
}
[GeneratedRegex("\\.(zip|tar|7z|rar|zipx)$")]
private static partial Regex ArchiveRegex();
}
}
但是例如如果我想使用LibaryImport,它会得到错误,就像图像
WIN32_FIND_DATA在其他类中
public class AdditionalInfo
{
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
public struct WIN32_FIND_DATA
{
public FileAttributes dwFileAttributes;
public uint ftCreationTime_dwLowDateTime;
public uint ftCreationTime_dwHighDateTime;
public uint ftLastAccessTime_dwLowDateTime;
public uint ftLastAccessTime_dwHighDateTime;
public uint ftLastWriteTime_dwLowDateTime;
public uint ftLastWriteTime_dwHighDateTime;
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;
}
}
我只是使用 LibraryImportAttribute 来获得更好的性能
尝试将
CharSet
替换为 StringMarshalling
文档列出了以下差异:
CallingConvention 在 LibraryImportAttribute 上没有等效项。应改用 UnmanagedCallConvAttribute。
CharSet(对于 CharSet)已替换为 StringMarshalling(对于 StringMarshalling)。 ANSI 已被删除,UTF-8 现在可作为一流选项。
BestFitMapping 和 ThrowOnUnmappableChar 没有等效项。这些字段仅在 Windows 上编组 ANSI 字符串时相关。用于编组 ANSI 字符串的生成代码将具有与 BestFitMapping=false 和 ThrowOnUnmappableChar=false 等效的行为。
ExactSpelling 没有等效项。该字段是以 Windows 为中心的设置,对非 Windows 操作系统没有影响。方法名称或 EntryPoint 应与入口点名称的拼写完全相同。该字段的历史用途与 Win32 编程中使用的 A 和 W 后缀相关。
PreserveSig 没有等效项。该字段是以 Windows 为中心的设置。生成的代码总是直接翻译签名。