10

I'm trying to make my image viewer app work with files that are opened by clicking an image file directly inside a Zip folder (using windows explorer to browse Zip files). The application seems to be run with correct command line, which looks like this:

"C:\myApp.exe" "C:\Users\Admin\AppData\Local\Temp\Temp1_Wallpapers.zip\Wallpaper1.jpg"

The file is being read with the following code:

using (var fs = new FileStream(path, FileMode.Open))

And the exception is thrown at that line:

Exception:Thrown: "Access to the path 'C:\Users\Admin\AppData\Local\Temp\Temp1_Wallpapers.zip\Wallpaper1.jpg' is denied." (System.UnauthorizedAccessException)

A System.UnauthorizedAccessException was thrown: "Access to the path 'C:\Users\Admin\AppData\Local\Temp\Temp1_Wallpapers.zip\Wallpaper1.jpg' is denied."

I figured this may be a problem with how the path is interpreted. There's a .zip in the middle of it, so this could be the problem, but I don't know how to solve that.

Also, simply opening a file at that path directly (not through zipped folder explorer window) results in the same exception.

Community
  • 1
  • 1
user1306322
  • 8,561
  • 18
  • 61
  • 122

3 Answers3

6

Windows Explorer gains the ability to treat a .zip archive as a folder through a shell name extension handler. Such handlers extend the capability of the shell. But that's however restricted to shell functions only, it doesn't automagically make the low-level file access functions capable of doing the same. Like FileStream.

You'll need to copy the file out of the .zip archive first, then you can open it with FileStream. Lots of .zip support libraries around, SharpZipLib and DotNetZip are popular. It got finally added to .NET 4.5 with the System.IO.Compression.ZipArchive class. Let's pick that one for the most future-proof example code.

I created an Example.zip archive with a single image and copied it to my temp directory. This code retrieved it and made it the background image of a Winforms form:

using System.IO;
using System.IO.Compression;    // Add reference to System.IO.Compression
...

     private void button1_Click(object sender, EventArgs e) {
        var srcePath = @"c:\users\hpass_000\appdata\local\temp\example.zip";
        using (var file = new FileStream(srcePath, FileMode.Open)) {
            var zip = new ZipArchive(file, ZipArchiveMode.Read);
            var entry = zip.GetEntry("Chrysanthemum.jpg");
            var destPath = Path.GetTempFileName();
            using (var srce = entry.Open())
            using (var dest = new FileStream(destPath, FileMode.Create)) {
                srce.CopyTo(dest);
            }
            using (var img = Image.FromFile(destPath)) {
                this.BackgroundImage = new Bitmap(img);
            }
            File.Delete(destPath);
        }
    }
Hans Passant
  • 922,412
  • 146
  • 1,693
  • 2,536
  • It appears that the full path is contained in a normal folder, it's just called ".zip", but it is not a zip folder. Usually path to contents of a zipped folder don't display ".zip" in the full path bar, so it's not the case. This is why I wonder why it doesn't work. – user1306322 Aug 10 '13 at 19:26
  • 2
    It is entirely normal that it doesn't work, that was the point I tried to make in the answer. Sounds like you completely missed it. Not really sure how to help you over that hump, I'd recommend you do the things I recommended. – Hans Passant Aug 10 '13 at 19:34
  • This temp folder, that has the word "temp" 2 times within its full path, looks like an ordinary folder, compared to browsing a zipped folder with windows explorer. I don't know how else I can convey that I'm almost certain that this isn't what you think it is. – user1306322 Aug 10 '13 at 21:04
  • 2
    Right, it only **looks** like a folder. Windows Explorer creates that illusion. It is not, it is really a plain file. You'll have to write the code to extract the file from the archive. I posted a sample implementation. – Hans Passant Aug 10 '13 at 21:36
  • That's why I said *compared* to browsing a zipped folder. I could get you some screenshots to show the difference, but I'm not sure if it will get me any closer to solving the actual problem. – user1306322 Aug 10 '13 at 21:40
  • While Explorer does have namespace extensions, I'm sure it must be doing an extraction operation on the backend before it shellexecutes a file, because most applications aren't aware of the namespace extensions. – EricLaw Aug 16 '13 at 06:28
2

I just found out what the problem was. Files extracted from compressed folders into temp folder will have read-only attribute, which my image viewer app apparently can't handle and throws UnauthorizedAccessException. I just need to remove that attribute and everything will be fine. Guess trying to read read-only files is an access violation of sorts.

user1306322
  • 8,561
  • 18
  • 61
  • 122
  • 2
    No, reading read-only files isn't unauthorized, but the code that you're using to read them is asking for excessive (read/write) permissions. Fix that and the error goes away. – EricLaw Aug 16 '13 at 06:27
  • @EricLaw any hints on how to do that? – user1306322 Aug 16 '13 at 09:47
  • 2
    Try adding a third parameter: `FileAccess.Read` to the FileStream constructor. It's kind of silly that .Net goes for the most restrictive (`ReadWrite`) permission by default, although I guess otherwise the forums would be full of "Why can't I write to my filestream!?!?!" – nemec Aug 18 '13 at 04:44
  • @nemec that didn't help, but for now I just resorted to setting file attributes to `Normal`, and it works. – user1306322 Aug 18 '13 at 06:28
1

The issue has nothing to do with the . in the temp file path because periods are legal in file names as well as directory names.

As you expect, opening a Zip folder in the shell and opening a file automatically extracts the contents to a temp folder, which is just a normal folder. The only thing that looks strange here is that it's opening the Administrator temp folder. Are you running the exe as a normal user? If the exe and shell are running under separate users, the exe may not be able to access the temp folder used by the shell.

nemec
  • 1,044
  • 9
  • 35