在 C# 中将 FILE_NAME_INFORMATION 转换为字符串
Posted
技术标签:
【中文标题】在 C# 中将 FILE_NAME_INFORMATION 转换为字符串【英文标题】:Converting FILE_NAME_INFORMATION to String in C# 【发布时间】:2016-02-12 06:26:53 【问题描述】:我在 C# 中使用函数 NtQueryInformationFile 来获取有关并打开函数返回的文件句柄的信息。这个函数给了我一个指向以下结构的指针:
typedef struct _FILE_NAME_INFORMATION
ULONG FileNameLength;
WCHAR FileName[1];
FILE_NAME_INFORMATION, *PFILE_NAME_INFORMATION;
地点:
FileNameLength 指定文件名的长度,以字节为单位 细绳。 FileName 指定文件名的第一个字符 细绳。字符串的其余部分在内存中紧随其后。
如何在 C# 中将其转换为字符串?
【问题讨论】:
到目前为止你写过什么代码吗? 请看答案 【参考方案1】:我认为你正在寻找的术语是“Marshaling”,因此这个方法应该做你想做的事:
https://msdn.microsoft.com/en-US/library/ms146626(v=vs.110).aspx
char[] managedArray = new char[yourDataObject.FileNameLength];
Marshal.Copy(yourDataObject.FileName, managedArray, 0, managedArray.Length);
// and now build your string with your managed array
// using Stringbuilder order something like the below
string myString = new string(managedArray);
【讨论】:
我试过了,但它一直在崩溃:IntPtr iprc = NtQueryInformationFile(FileHandle, ref iosb, p_fbi, (uint)Marshal.SizeOf(fbi), FILE_INFORMATION_CLASS.FileNamesInformation); FILE_NAME_INFORMATION fbi = (FILE_NAME_INFORMATION)Marshal.PtrToStructure(p_fbi, typeof(FILE_NAME_INFORMATION)); var bytDest = new byte[fbi.FileNameLength+3]; Marshal.Copy(p_fbi, bytDest, 0, (int)fbi.FileNameLength); bytDest.ToString()
抱歉忘记了一些事情:你需要传递数组而不是指针:Marshal.Copy(yourDataObject.FileName, managedArray, 0, managedArray.Length);
所以你的代码应该是Marshal.Copy(fbi.Filename, bytDest, 0, (int)fbi.FileNameLength);
它不断给我访问冲突错误,所以我不得不使用另一种方法......【参考方案2】:
您可以根据结构中包含的字符类型使用适当的 Marshal.CopyPtrToString 变体。
【讨论】:
这不是很丰富。怎么没用?鉴于结构的定义,字符串可能不是以零结尾的,因此您需要使用 CopyPtrToString 的重载,同时获取指向 char 数组的指针和字符串的长度。 我发现切换到下面的命令更容易,而不是在字符串转换上浪费太多时间。 您发布的 StringBuilder 代码?它可能会编组为 char 数组,但在将其传递给非托管代码之前,您仍然需要确保其容量足以容纳 MAX_PATH 字符(262,iIrc,无需过度使用 10k)。 你是对的。我刚刚将缓冲区大小更改为 1000,我认为这是一个安全的选择【参考方案3】:我改用了这个,它在托管代码中更稳定:
var sbPath = new StringBuilder();
if (FileHandle == INVALID_HANDLE_VALUE)
throw (new Exception("Invalid file handle!"));
GetFinalPathNameByHandle(FileHandle, sbPath, 10000, FILE_NAME_NORMALIZED);
【讨论】:
嗯,这不太对。您正在创建一个 emptyStringBuilder
,然后告诉该函数它指向一个缓冲区 10000
字符长。这几乎肯定会爆炸。您可以在创建StringBuilder
时尝试猜测所需的长度(MAX_PATH
是一个合理的选择),然后处理缓冲区太小的情况。 (另一个大罪:你没有检查那个函数的结果:你怎么知道它是否失败?)
你是对的,这只是解决方案的基本版本。我认为我应该发布一个更完整的。
我不知道你所说的“基本”是什么意思。这是完全错误的。我所说的“不太正确”的意思是你有这个想法,但你的代码注定要彻底失败。 StringBuilder
未初始化,因此 GetFinalPathNameByHandle
函数将无法写入它,然后更糟糕的是,您对函数撒谎并告诉它您已向其传递了一个长度为 10000 个字符的缓冲区.如果您已经知道这一点,但无论如何发布了此代码,那么不,您不应该这样做。示例代码不必进行错误检查或其他什么,但它至少应该工作。
我可以向您保证,它完全可以正常工作,没有任何抛出的异常或意外行为。我已将它用作 NtQueryDirectoryFile 函数的钩子的一部分,以获取当前文件夹的名称,并且效果很好。如果你愿意,我什至可以用图片/视频向你展示。
令人难以置信。我想你会变得非常幸运。我仔细查看了文档,结果发现StringBuilder
的默认构造函数将其容量初始化为“特定于实现的默认容量”。你的猜测和我的猜测一样好,但显然它足够长来保存GetFinalPathNameByHandle
试图返回给你的字符串。不要指望那种运气。路径长度会随时更改,您不能依赖“特定于实现”的行为。以上是关于在 C# 中将 FILE_NAME_INFORMATION 转换为字符串的主要内容,如果未能解决你的问题,请参考以下文章