DllImport kernel32.dll 代码在控制台应用程序中工作但在 UWP 中不工作 - FindFirstFileW returns INVALID_HANDLE_VALUE

DllImport kernel32.dll code working in console app but not in UWP - FindFirstFileW returns INVALID_HANDLE_VALUE

我第一次尝试在 UWP 应用程序中使用低级别 Windows API(特别是 FindFirstFileW / fileapi.h)。

我在 .Net 控制台应用程序中成功获得了概念验证代码 运行,现在想在 UWP 应用程序(个人爱好项目)中尝试它。导入语句使用 SetLastError = true:

[DllImport("kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
public static extern IntPtr FindFirstFileW(string lpFileName, out WIN32_FIND_DATAW lpFindFileData);

UWP 代码编译正常,运行时没有抛出任何错误,但是 FindFirstFileW(lpFileName, out findData) returns INVALID_HANDLE_VALUE,而控制台应用 returns 文件系统数据。

我还没有按照 https://docs.microsoft.com/en-us/windows/uwp/files/file-access-permissions#accessing-additional-locations 中的描述添加 <rescap:Capability Name="broadFileSystemAccess" />,因为我收到警告:

The element 'Capabilities' in namespace 'http://schemas.microsoft.com/appx/manifest/foundation/windows10' has invalid child element 'Capability' in namespace 'http://schemas.microsoft.com/appx/manifest/foundation/windows10/restrictedcapabilities'.

很高兴提供更多信息。

更新 - 复制错误的示例项目:

完整 NativeDirectoryScanner.cs

using System;
using System.Collections.Generic;
using System.IO;
using System.Runtime.InteropServices;

namespace FileAPITest
{
    public static class FILETIMEExtensions
    {
        public static DateTime ToDateTime(this System.Runtime.InteropServices.ComTypes.FILETIME filetime)
        {
            long highBits = filetime.dwHighDateTime;
            highBits = highBits << 32;
            return DateTime.FromFileTimeUtc(highBits + (long)filetime.dwLowDateTime);
        }
    }

    public class NativeDirectoryScanner
    {
        [DllImport("kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
        public static extern IntPtr FindFirstFileW(string lpFileName, out WIN32_FIND_DATAW lpFindFileData);

        [DllImport("kernel32.dll", CharSet = CharSet.Unicode)]
        public static extern bool FindNextFile(IntPtr hFindFile, out WIN32_FIND_DATAW lpFindFileData);

        [DllImport("kernel32.dll")]
        public static extern bool FindClose(IntPtr hFindFile);

        [DllImport("kernel32.dll")]
        static extern uint GetLastError();

        [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
        public struct WIN32_FIND_DATAW
        {
            public FileAttributes dwFileAttributes;
            internal System.Runtime.InteropServices.ComTypes.FILETIME ftCreationTime;
            internal System.Runtime.InteropServices.ComTypes.FILETIME ftLastAccessTime;
            internal System.Runtime.InteropServices.ComTypes.FILETIME ftLastWriteTime;
            public int nFileSizeHigh;
            public int nFileSizeLow;
            public int dwReserved0;
            public int dwReserved1;
            [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 260)]
            public string cFileName;
            [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 14)]
            public string cAlternateFileName;
        }

        public class Info
        {
            public bool IsDirectory;
            public string Path;
            public DateTime ModifiedDate;
            public DateTime CreatedDate;
        }

        public static List<Info> RecursiveScan2(string directory)
        {
            IntPtr INVALID_HANDLE_VALUE = new IntPtr(-1);
            WIN32_FIND_DATAW findData;
            IntPtr findHandle = INVALID_HANDLE_VALUE;
            //FolderInfo folderInfo;
            string lpFileName = string.Empty;

            //var info = new List<FolderInfo>();
            var list = new List<Info>();
            try
            {
                //lpFileName = directory + @"\*";
                lpFileName = directory + "*";

                list.Add(new Info()
                {
                    CreatedDate = DateTime.Now,
                    ModifiedDate = DateTime.Now,
                    IsDirectory = false,
                    Path = string.Format("RecursiveScan2.lpFileName (80): {0}", lpFileName)
                });



                findHandle = FindFirstFileW(lpFileName, out findData);

                list.Add(new Info()
                {
                    CreatedDate = DateTime.Now,
                    ModifiedDate = DateTime.Now,
                    IsDirectory = false,
                    Path = string.Format("RecursiveScan2.GetLastError: {0}", GetLastError().ToString())
                });

                if (findHandle != INVALID_HANDLE_VALUE)
                {

                    do
                    {
                        if (findData.cFileName == "." || findData.cFileName == "..") continue;

                        string fullpath = directory + (directory.EndsWith("\") ? "" : "\") + findData.cFileName;

                        bool isDir = false;

                        if ((findData.dwFileAttributes & FileAttributes.Directory) != 0)
                        {
                            isDir = true;
                            list.AddRange(RecursiveScan2(fullpath));
                        }

                        list.Add(new Info()
                        {
                            CreatedDate = findData.ftCreationTime.ToDateTime(),
                            ModifiedDate = findData.ftLastWriteTime.ToDateTime(),
                            IsDirectory = isDir,
                            Path = fullpath
                        });
                    }
                    while (FindNextFile(findHandle, out findData));

                }
            }
            catch (Exception ex)
            {
                list.Add(new Info()
                {
                    CreatedDate = DateTime.Now,
                    ModifiedDate = DateTime.Now,
                    IsDirectory = false,
                    Path = string.Format("RecursiveScan2.lpFileName (131): {0}", lpFileName)
                });
                list.Add(new Info()
                {
                    CreatedDate = DateTime.Now,
                    ModifiedDate = DateTime.Now,
                    IsDirectory = false,
                    Path = string.Format("RecursiveScan2.Error: {0}", ex.Message)
                });
            }
            finally
            {
                if (findHandle != INVALID_HANDLE_VALUE) FindClose(findHandle);
            }
            return list;
        }


    }
}

===========================================

Xaml片段:

<RelativePanel>
    <Button x:Name="btnGo" Content="Go" Margin="5" Tapped="BtnGo_Tapped" />
    <TextBox x:Name="txtInput" Margin="5" RelativePanel.RightOf="btnGo" RelativePanel.AlignRightWithPanel="True" Text="C:/" />
    <TextBlock x:Name="txtoutput" Margin="5,0,5,5" RelativePanel.Below="btnGo" RelativePanel.AlignLeftWithPanel="True" RelativePanel.AlignRightWithPanel="True" RelativePanel.AlignBottomWithPanel="True" />
</RelativePanel>

===========================================

xaml.cs

private void BtnGo_Tapped(object sender, TappedRoutedEventArgs e)
{
    StringBuilder sb = new StringBuilder();

    try
    {
        List<Info> list = RecursiveScan2(txtInput.Text);

        for(int i = 0; i < list.Count; i++)
        {
            sb.AppendFormat("Item: {2} - {1}{0}", Environment.NewLine, list[i].Path, i);
        }
    }
    catch(Exception ex)
    {
        sb.AppendFormat("{0}Error: {1}{0}Stacktrace: {2}", Environment.NewLine, ex.Message, ex.StackTrace);
    }

    txtoutput.Text = sb.ToString();
}

我试了代码,代码returns和你说的结果一样。 FindFirstFileWdocument 提到此 API 可以在 UWP 应用程序中使用,但它仍将受到 UWP 应用程序的限制,因为 UWP 运行 在沙盒中。

如果您要查找文件及其句柄,还有另一种方法可以做到这一点。您可以使用 IStorageItemHandleAccess interface or from a StorageFolder with the IStorageFolderHandleAccess interface 从 StorageFile 获取代理文件 HANDLE。获得句柄后,您应该可以在某些 win32 API 中使用句柄来执行您想要的操作。

此外,如果您不介意使用桌面桥接器,您可以在桌面应用程序中使用,然后将其与 UWP 应用程序一起打包。使用 UWP 应用程序启动调用 win32 APIs 来完成工作的桌面应用程序。