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/uwp/win32-and-com/win32-apis 好像可以调用FindFirstFileW
- 我的 UWP 应用面向 v 1803(10.0;内部版本 17134),最低版本创意者更新(10.0;内部版本 15063)。
Settings -> Privacy -> File system
已开启。
我还没有按照 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和你说的结果一样。 FindFirstFileW
的 document 提到此 API 可以在 UWP 应用程序中使用,但它仍将受到 UWP 应用程序的限制,因为 UWP 运行 在沙盒中。
如果您要查找文件及其句柄,还有另一种方法可以做到这一点。您可以使用 IStorageItemHandleAccess interface or from a StorageFolder with the IStorageFolderHandleAccess interface 从 StorageFile 获取代理文件 HANDLE。获得句柄后,您应该可以在某些 win32 API 中使用句柄来执行您想要的操作。
此外,如果您不介意使用桌面桥接器,您可以在桌面应用程序中使用,然后将其与 UWP 应用程序一起打包。使用 UWP 应用程序启动调用 win32 APIs 来完成工作的桌面应用程序。
我第一次尝试在 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/uwp/win32-and-com/win32-apis 好像可以调用FindFirstFileW
- 我的 UWP 应用面向 v 1803(10.0;内部版本 17134),最低版本创意者更新(10.0;内部版本 15063)。
Settings -> Privacy -> File system
已开启。
我还没有按照 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和你说的结果一样。 FindFirstFileW
的 document 提到此 API 可以在 UWP 应用程序中使用,但它仍将受到 UWP 应用程序的限制,因为 UWP 运行 在沙盒中。
如果您要查找文件及其句柄,还有另一种方法可以做到这一点。您可以使用 IStorageItemHandleAccess interface or from a StorageFolder with the IStorageFolderHandleAccess interface 从 StorageFile 获取代理文件 HANDLE。获得句柄后,您应该可以在某些 win32 API 中使用句柄来执行您想要的操作。
此外,如果您不介意使用桌面桥接器,您可以在桌面应用程序中使用,然后将其与 UWP 应用程序一起打包。使用 UWP 应用程序启动调用 win32 APIs 来完成工作的桌面应用程序。