这段PInvoke代码是否正确可靠?

22
这个问题中,我寻找了一个简单的解决方案来解封文件。 感谢所有的评论和答案,我通过 PInvoking DeleteFile 找到了一个简单的解决方案。
它有效,但由于我从未使用过 PInvoke(Win32)进行文件操作,我不知道是否存在一些陷阱或者是否有其他调用 DeleteFile 删除文件备用流的方法。
我还不知道是否需要将调用包装在 try/catch 中,或者只需查看布尔结果是否足够。在我的测试中没有引发异常,但我不知道在现实世界中会发生什么。
public class FileUnblocker {

    [DllImport("kernel32", CharSet = CharSet.Unicode, SetLastError = true)]
    [return: MarshalAs(UnmanagedType.Bool)]
    public static extern bool DeleteFile(string name );

    public bool Unblock(string fileName) {
        return DeleteFile(fileName+ ":Zone.Identifier");
    }
}

这段代码看起来可靠吗?

更新
我之前发布的方法是不完整的(unblock方法没有将"Zone.Identifier"链接到文件名)。我已经纠正了这个问题,抱歉。


2
对于那些盲目地给答案投反对票的人,请你们有礼貌地解释一下为什么这个答案是错误的。 - Chris Eberle
@Chris,实际上我想知道的是谁点赞了这两个答案:1)除了给出与问题中相同的P/Invoke签名外,什么也没做,在某种情况下使用Ansi字符串;2)没有以任何方式回答问题。 - David Heffernan
我做到了,你永远也抓不到我。Muah hah hah. >:) - Chris Eberle
1
StackOverflow不是一个代码审查网站。CodeReview.SE才是。 - Sam Axe
2
@SamAxe 代码审查不是用来审查代码可靠性的网站,StackOverflow才是。 - Ismael Miguel
显示剩余2条评论
3个回答

20

调用本地方法永远不会引发异常。如果文件删除失败,无论任何原因,调用DeleteFile将返回false。

你的P/Invoke代码很好。你正确使用Unicode字符,将SetLastError设置为true,参数封送是正确的。要检查错误,请查找从DeleteFile返回的布尔值。如果它为false(即调用失败),则调用Marshal.GetLastWin32Error来查找Win32错误代码。

该函数失败的最明显原因是:

  1. 文件不存在。
  2. 备用数据流不存在。
  3. 进程没有足够的权限删除备用数据流。

对于情况1和2,将返回错误代码ERROR_FILE_NOT_FOUND。对于情况3,将给出错误代码ERROR_ACCESS_DENIED


1
抱歉,我太匆忙了,没有在发布代码时提到我忘记在Unblock方法中添加流标识符。我已经编辑了我的帖子。很抱歉,通常我会尽量避免发布低质量的问题,浪费别人的时间。下次我会更加小心。 - HCL
1
@HCL 是的,我有点困惑。不过我现在觉得我已经全面考虑到了! - David Heffernan

8

我对代码进行了小的改进。现在,您只需将启动路径传递给UnblockPath()函数,它将自动解除执行文件的所有文件和子目录文件的阻止。可以进一步优化,仅搜索.exe、.dll等文件。

[DllImport("kernel32", CharSet = CharSet.Unicode, SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool DeleteFile(string name);

public static void UnblockPath(string path)
{
    string[] files = System.IO.Directory.GetFiles(path);
    string[] dirs = System.IO.Directory.GetDirectories(path);

    foreach (string file in files)
    {
        UnblockFile(file);
    }

    foreach (string dir in dirs)
    {
        UnblockPath(dir);
    }

}

public static bool UnblockFile(string fileName)
{
    return DeleteFile(fileName + ":Zone.Identifier");
}

3
using System;
using System.IO;
using System.Runtime.InteropServices;
using System.Windows.Forms;

internal class Zone
{
    public static void WriteAlternateStream(string path, string text)
    {
        const int GENERIC_WRITE = 1073741824;
        const int FILE_SHARE_WRITE = 2;
        const int OPEN_ALWAYS = 4;
        var stream = CreateFileW(path, GENERIC_WRITE, FILE_SHARE_WRITE, IntPtr.Zero, OPEN_ALWAYS, 0, IntPtr.Zero);
        using (FileStream fs = new FileStream(stream, FileAccess.Write))
        {
            using (StreamWriter sw = new StreamWriter(fs))
            {
                sw.Write(text);
            }
        }
    }
    public static void Id()
    {
        var x = Application.ExecutablePath + ":Zone.Identifier";
        WriteAlternateStream(x, "[ZoneTransfer]\r\nZoneId=3");
    }
    # region Imports
    [DllImport("kernel32.dll", EntryPoint = "CreateFileW")]
    public static extern System.IntPtr CreateFileW(
        [InAttribute()] [MarshalAsAttribute(UnmanagedType.LPWStr)] string lpFileName,
        uint dwDesiredAccess,
        uint dwShareMode,
        [InAttribute()] IntPtr lpSecurityAttributes,
        uint dwCreationDisposition,
        uint dwFlagsAndAttributes,
        [InAttribute()] IntPtr hTemplateFile
    );
    #endregion
}

2
这个操作相反 - 锁定文件。虽然与问题无关,但仍然很有用,谢谢。 - Andrey Klochkov

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接