0

enter image description hereWhat am I doing wrong?

I have a read/copy/write process that access a folder that is installed with the software.

This worked great when testing the build, but once the solution was created it completely stopped working. What am I doing wrong?

(breakdown of code) I essentially access a PDF called CTW.pdf Next we create a temp copy which the software fills in and flatted On completion, the software saves the new copy to the same directory with a new name (i simply add the time and date to the file name)

Here's the code:

if (screenLock.Text == "")
{

}
else
{
    string fileNameExisting = @"C:\Program Files (x86)\Compliance Pro 2\CTW.pdf";
    string fileNameNew = @"C:\Program Files (x86)\Compliance Pro 2" + " CTW.pdf" + DateTime.Now.ToString("M/d/yyyy") + DateTime.Now.ToString("hh:mm:ss");

    using (var existingFileStream = new FileStream(fileNameExisting, FileMode.Open))
    using (var newFileStream = new FileStream(fileNameNew, FileMode.Create))
    {
        // https://www.codeproject.com/Tips/679606/Filling-PDF-Form-using-iText-PDF-Library 
        // Open existing PDF
        var pdfReader = new PdfReader(existingFileStream);
        // PdfStamper, which will create
        var stamper = new PdfStamper(pdfReader, newFileStream);
        var form = stamper.AcroFields;
        var fieldKeys = form.Fields.Keys;
        foreach (string fieldKey in fieldKeys)
        {
           form.SetField(fieldKey, "");
        }
        stamper.AcroFields.SetField("OSJNonOSJ", "TFSN");
        stamper.AcroFields.SetField("Branch", "9DCF");
        stamper.AcroFields.SetField("User Name Last First", "Joe" + "Pearson");
        stamper.AcroFields.SetField("Tested by Last First", emailTo.Text);
        stamper.AcroFields.SetField("Date Tested", DateTime.Now.ToString("M/d/yyyy"));
        stamper.AcroFields.SetField("COMMENTS", notes.Text);
        stamper.AcroFields.SetField("WDE-Yes", user.Text + "5");
    }
}

NOTE: I have worked really hard to ensure my software does not require ANY administrative access to complete it's functions, I would really like to avoid that here as well.

Rufus L
  • 36,127
  • 5
  • 30
  • 43
Joe Pearson
  • 105
  • 10
  • i have added an image for reference – Joe Pearson Sep 13 '17 at 18:56
  • 1
    Usually the C:\ drive does require admin privileges to write to it. And that's probably why you get the `AccessDenied` error? – Sach Sep 13 '17 at 18:56
  • Is there a cleaner solution possible? I simply wish to pull this PDF, write over it, and save it somewhere (i don't care where I save it, it can even be user-defined) – Joe Pearson Sep 13 '17 at 18:57
  • First try to figure out what your problem is. Here's a few tips; A) Open/Save the file from a location that your account has access to. Then, B) Use the C:\ location but try to run the code from an admin account. This will let you verify whether its a permission problem, which almost certainly it is. – Sach Sep 13 '17 at 19:00
  • According to your screen shot, the offending line is the one where you try to open the existing file, not where you try to write the new file. Try using the overload that allows you to specify your access level: `using (var existingFileStream = new FileStream(fileNameExisting, FileMode.Open, FileAccess.Read))` - is it possible the file is open / locked by another process? – stephen.vakil Sep 13 '17 at 19:01
  • In alternative locations (external server location which I have admin access to from this account) It saves just fine. However, users wont have access to that folder. So naturally, I need a protected areas for the dependent PDF to be hosted every time the software creates a copy. suggestions? – Joe Pearson Sep 13 '17 at 19:02
  • 6
    You **shouldn't** be writing to **Program Files** xxx at runtime ever since Vista came out. Write to **Program Data** or **AppData** instead. Even if it did work before, it's not correct policy –  Sep 13 '17 at 19:02
  • How can I go about doing that? – Joe Pearson Sep 13 '17 at 19:05
  • @JoePearson please [check this out](https://msdn.microsoft.com/en-us/library/system.environment.specialfolder(v=vs.110).aspx). You can get a multitude of different system paths using this. – Sach Sep 13 '17 at 19:08
  • You should consider if (screenLock.Text != "") and get rid of the else. Empty block chains are bad practice. – Sam Marion Sep 13 '17 at 19:08
  • @SamMarion I think you mean `if (screenLock.Text != "")`. Another common option would be `if (!string.IsNullOrWhitespace(screenLock.Text))` – Rufus L Sep 13 '17 at 19:11
  • Hhaha indeed, its been a long day thanks I"ll edit – Sam Marion Sep 13 '17 at 19:12
  • Has anyone ever written to a file that their software expressly included in it's build? It would help a lot if I could see what solution they found for this kind of issue. – Joe Pearson Sep 13 '17 at 19:12
  • @Sach It's definitely an Admin issue. I was able to determined that from the "Access denied" popup i get when running a completed build instead of the test build. – Joe Pearson Sep 13 '17 at 19:14
  • “the software saves the new copy to the same directory” Stop doing that; Standard users cannot write to the Program Files, and even if they could when they uninstall or repair the app the folder gets overwritten. Save all user data to `Path.Combine(Environment.GetFolderPath(SpecialFolder.ApplicationData), yourApplicationName, yourFilename)` – Dour High Arch Sep 13 '17 at 20:51
  • @DourHighArch could you show me an example of how that would look? – Joe Pearson Sep 13 '17 at 20:53
  • Check out [program can't find file after install](https://stackoverflow.com/a/13296809/22437). – Dour High Arch Sep 13 '17 at 22:14

2 Answers2

1

Putting the file directly into C:\ or even trying to rwrite there was a poor choice. Practically every relevant folder of the Windows Drive is protected, so you will need at least Adminsitrative Privileges to write to them. Without those, you can not even asume write rights to the programm directory (and even if you can do that, you really should not be placing data there).

If you want a place where you can write and store you data, look to the SpecialFolders. The Userprofile specific folders in particular are designed so you should have access as a normal user. Use the Publics ones if the data is shared between Users, otherwise the Profile ones.

Christopher
  • 9,634
  • 2
  • 17
  • 31
  • 1
    What about the actual error in question, though, which was *reading* from a file under `%ProgramFiles(x86)%`? – Rufus L Sep 13 '17 at 19:15
  • Is there a location that is best suited for hosting the software directory? Maybe you have a preferred folder that you have had success with? – Joe Pearson Sep 13 '17 at 19:16
  • Could be that you ran into Virtualsiation (http://www.thecodeking.co.uk/2007/03/making-your-application-uac-aware.html). Really anything is possible because you tried to access that folder in the first place. You should never even try, except for the installer. Just put the file into the SpecialFolders belonging to the user and you will have 0 issues with access. If there ever is a issue, it is propably adminsitration issue. – Christopher Sep 13 '17 at 19:18
  • what If ii went straight the user\documents? is that possible? – Joe Pearson Sep 13 '17 at 19:20
  • If you use the SpecialFolders, getting to that folder is trivial. You can find it via the "MyDocuments" key. And as I said already, having full rights to those from any programm run by this user is kind of their purpose. – Christopher Sep 13 '17 at 19:23
  • If only one login is using the data try %appdata% - not sure what the evilent would be for all users, maybe %ProgramData% – rheitzman Sep 13 '17 at 19:26
  • I'm spacing on this lol could someone provide a sample? I already collect the user and device info wit the software so I would have the ability to programatically access the user's folders. – Joe Pearson Sep 13 '17 at 19:27
  • Jsut look here: https://msdn.microsoft.com/en-us/library/system.environment.specialfolder.aspx#Examples under Example. Do not try to be smarter then that, you will only run into issue trying to build that path yourself. – Christopher Sep 13 '17 at 19:28
1

The line in error is the line trying to read the existing file.

You are getting the specified error because you are reading from a protected location but not specifying that you only want to read data. It is trying to lock the file, which requires elevated privileges.

Change your line for the reading operation to use FileAccess.Read. It should clear that hurdle.
I get this same error when I create a local console app and don't run as administrator. But it works fine if I add the below code.

using (var existingFileStream = new FileStream(fileNameExisting, FileMode.Open, FileAccess.Read))

Then you will need to determine how best to write the file, which should not be done in Program Files at all for reasons mentioned by others.

stephen.vakil
  • 3,492
  • 1
  • 18
  • 23
  • Though a good spot, I suspect it will just fail on the next line where the OP attempts to open the second file with the intent of **writing** –  Sep 13 '17 at 20:52
  • I don't see any mention in the [docs](https://msdn.microsoft.com/en-us/library/47ek66wy) about file locking. But it's stated that the constructor without the `access` parameter defaults to `ReadWrite` access. This requires an administrator logon unless the security descriptor was set otherwise by the application's installer. – Eryk Sun Sep 14 '17 at 01:13