Является ли этот код PInvoke правильным и надежным?

В этот вопрос Я искал простое решение для разблокирования файлов. Благодаря всем комментариям и ответам я нашел простое решение от PInvoking DeleteFile.

Это работает, но поскольку я никогда не использовал файловые операции через PInvoke (Win32), я не знаю, есть ли какие-то подводные камни или есть другой метод вызова DeleteFile для удаления альтернативного потока файл.

То, что я также не знаю, - это если мне нужно обернуть вызов в try/catch или достаточно просто посмотреть на результат boolean. В моих тестах никаких исключений не было, но я не знаю, что произойдет в реальном мире.

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");
    }
}

Является ли этот код надежным?

Обновление
Я опубликовал неполный метод (метод разблокирования не связал литерал "Zone.Identifier" с именем файла). Я исправил это сейчас, извините.

Ответ 1

Вызов метода native никогда не вызовет исключения. Если удаление файла завершается неудачно, по какой-либо причине вызов DeleteFile возвращает false.

Ваш код P/Invoke хорош. Вы правильно используете символы Юникода, установив SetLastError в true и правильная сортировка параметров. Чтобы проверить наличие ошибок, найдите значение логического возврата из DeleteFile. Если это неверно (т.е. Вызов не удался), вызовите Marshal.GetLastWin32Error, чтобы узнать код ошибки Win32.

Наиболее очевидными причинами отказа функции являются:

  • Файл не существует.
  • Альтернативный поток отсутствует.
  • Процесс не имеет достаточных прав для удаления альтернативного потока.

Для 1 и 2 будет возвращен код ошибки ERROR_FILE_NOT_FOUND. Для 3 вам будет предоставлен код ошибки ERROR_ACCESS_DENIED.

Ответ 2

Я сделал небольшое уточнение для кода. Теперь вы можете просто передать свой путь запуска к функции 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
}