22

In this question I have searched for a simple solution to unblock files. Thanks to all the comments and answer, I have found a simple solution by PInvoking DeleteFile.

It works, but because I've never used file-operations through PInvoke (Win32), I don't know if there are some pitfalls or if there is another method of calling DeleteFile to delete the alternate stream of a file.

What I also don't know is if I have to wrap the call in a try/catch or if it is enough just to look the at the boolean result. In my tests, no exceptions were raised, but I don't know what will happen in the real world.

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

Does this code look reliable?

Update
I had posted an incomplete method (the unblock method didn't concatenate the "Zone.Identifier" literal to the file name). I have corrected this now, sorry.

Community
  • 1
  • 1
HCL
  • 36,053
  • 27
  • 163
  • 213
  • 2
    To whomever went through and blindly downvoted answers: have the courtesy of explaining why the answer was wrong. – Chris Eberle Jun 16 '11 at 18:34
  • @Chris Actually what I would like to know is who upvoted 2 answers that: 1) Did nothing more than give equivalent P/Invoke signatures to that in the question, and in one case using Ansi strings and 2) Did not address the question in any way. – David Heffernan Jun 16 '11 at 18:38
  • I did it, and you'll never catch me. Muah hah hah. >:) – Chris Eberle Jun 16 '11 at 18:40
  • @Chris It's better if downvoters leave comments explaining themselves, but really, up voting answers that are poor isn't beneficial to the Stack Overflow community. I do to some degree understand why some people downvote without comments because it's all to common to get revenge downvoting attacks. – David Heffernan Jun 16 '11 at 18:42
  • @David: interesting points. I have much to learn. – Chris Eberle Jun 16 '11 at 18:46
  • 1
    StackOverflow is not a code review site. CodeReview.SE is. – Sam Axe Jul 27 '15 at 23:02
  • 2
    @SamAxe Code Review is not a website to review the reliability of a code. StackOverflow is. – Ismael Miguel Jul 27 '15 at 23:04

3 Answers3

20

Calling the native method will never raise an exception. If the file deletion fails, for whatever reason, the call to DeleteFile returns false.

Your P/Invoke code is good. You are correctly using Unicode characters, setting SetLastError to true and the parameter marshalling is correct. To check for errors look for the value of the boolean return from DeleteFile. If it is false (i.e. the call failed) then call Marshal.GetLastWin32Error to find out the Win32 error code.

The most obvious causes for the function to fail are:

  1. The file does not exist.
  2. The alternate stream is not present.
  3. The process does not have sufficient rights to delete the alternate stream.

For 1 and 2 an error code of ERROR_FILE_NOT_FOUND will be returned. For 3 you will be given an error code of ERROR_ACCESS_DENIED.

David Heffernan
  • 601,492
  • 42
  • 1,072
  • 1,490
  • 1
    +1 Sorry, I was too much in a hurry to post the code and have not mentioned, that I have forgotten to add the stream identifier in the Unblock-method. I have edited my post. Sorry, generally I try not to post poor questions and waste time of people. I will take more care the next time. – HCL Jun 16 '11 at 18:50
  • 1
    @HCL Yes I was a bit thrown. I think I've covered all the bases now though! – David Heffernan Jun 16 '11 at 19:04
8

I made a small refinement to the code. You can now just pass your startup path to the UnblockPath() function and it will automatically unblock all of the files and sub directory files for your executable. It could be refined further to only search for .exe, .dll, etc.

[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");
}
John Goad
  • 81
  • 1
  • 1
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
}
TriX
  • 31
  • 2