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

时间:2011-06-16 16:49:08

标签: c# .net .net-4.0 pinvoke alternate-data-stream

this question中,我搜索了一个解锁文件的简单解决方案。 感谢所有的评论和回答,我找到了一个简单的解决方案: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”文字连接到文件名)。我现在纠正了这个,抱歉。

3 个答案:

答案 0 :(得分:17)

调用本机方法永远不会引发异常。如果文件删除失败,无论出于何种原因,对DeleteFile的调用都将返回false。

您的P / Invoke代码很好。您正确使用Unicode字符,将SetLastError设置为true并且参数编组正确。要检查错误,请查找DeleteFile的布尔值返回值。如果为false(即调用失败),则调用Marshal.GetLastWin32Error以找出Win32错误代码。

功能失败的最明显原因是:

  1. 该文件不存在。
  2. 不存在备用流。
  3. 该进程没有足够的权限删除备用流。
  4. 对于1和2,将返回ERROR_FILE_NOT_FOUND的错误代码。对于3,您将收到错误代码ERROR_ACCESS_DENIED

答案 1 :(得分:6)

我对代码做了一个小改进。您现在可以将启动路径传递给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");
}

答案 2 :(得分:1)

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
}