在 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);
        

【讨论】:

嗯,这不太对。您正在创建一个 empty StringBuilder,然后告诉该函数它指向一个缓冲区 10000 字符长。这几乎肯定会爆炸。您可以在创建StringBuilder 时尝试猜测所需的长度(MAX_PATH 是一个合理的选择),然后处理缓冲区太小的情况。 (另一个大罪:你没有检查那个函数的结果:你怎么知道它是否失败?) 你是对的,这只是解决方案的基本版本。我认为我应该发布一个更完整的。 我不知道你所说的“基本”是什么意思。这是完全错误的。我所说的“不太正确”的意思是你有这个想法,但你的代码注定要彻底失败。 StringBuilder 未初始化,因此 GetFinalPathNameByHandle 函数将无法写入它,然后更糟糕的是,您对函数撒谎并告诉它您已向其传递了一个长度为 10000 个字符的缓冲区.如果您已经知道这一点,但无论如何发布了此代码,那么不,您不应该这样做。示例代码不必进行错误检查或其他什么,但它至少应该工作 我可以向您保证,它完全可以正常工作,没有任何抛出的异常或意外行为。我已将它用作 NtQueryDirectoryFile 函数的钩子的一部分,以获取当前文件夹的名称,并且效果很好。如果你愿意,我什至可以用图片/视频向你展示。 令人难以置信。我想你会变得非常幸运。我仔细查看了文档,结果发现StringBuilder 的默认构造函数将其容量初始化为“特定于实现的默认容量”。你的猜测和我的猜测一样好,但显然它足够长来保存GetFinalPathNameByHandle 试图返回给你的字符串。不要指望那种运气。路径长度会随时更改,您不能依赖“特定于实现”的行为。

以上是关于在 C# 中将 FILE_NAME_INFORMATION 转换为字符串的主要内容,如果未能解决你的问题,请参考以下文章

在 C# 中将字符串转换为字节数组

如何在C#中将字符串转换为整数

在 C# 中将数据写入 CSV 文件

在 C# 中将 OracleDataReader 导出到 Excel

在 C# 中将对象数组转换为浮点数组 [关闭]

无法在 C# 中将类型 'byte[]' 隐式转换为 'byte?[]'