使用LibraryImport代替DllImport在编译时生成P/Invoke编组代码

问题描述 投票:0回答:1

我编写了一个获取目录树的函数。我目前正在使用 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,它会得到错误,就像图像enter image description here

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 来获得更好的性能

c# .net optimization compiler-errors compiler-optimization
1个回答
0
投票

尝试将

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 为中心的设置。生成的代码总是直接翻译签名。

© www.soinside.com 2019 - 2024. All rights reserved.