Является ли этот код 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
}