none
Streamwriter - Writing to user documents directory throws exception RRS feed

  • Question

  • Hi Guys,

    This will for sure be something very easy that I am missing, but I can't seem to find what I am doing wrong: The code I am struggling with is extremely simple:

    static StreamWriter Logfile = null;

    Logfile = new StreamWriter(args[4], true);

    When I pass in any "normal" directory like "C:\Temp\Test.txt" the code works perfectly fine.

    When I pass in "C:\Users\xxx\Documents\MyTool\Test.txt" (and yes, that directory exists, I checked the spelling multiple times...) I get a "System.IO.FileNotFoundException". I already tried giving everyone full control on the directory, but it doesn't change a thing. 

    Has anyone seen this before?

    thx

    Rick

    Friday, February 7, 2020 9:45 AM

Answers

  • If I would want to go specifically for those folders I would go through the Environment object anyway, the problem unfortunately started because one of the end users just dropped the program in his Documents folder and run it from there without specifying any paths. So args[4] was really just "Logfile.txt" (without the quotation marks) and was converted to <CurrentDirectory>\Logfile.txt. So even if I parsed for Documents or anything else, I wouldn't have found that.

    Well, now I at least have a stable workaround, so that's good enough. 

    I really appreciate your help Andrew, as well as the thoughts of everyone who stepped in. And I wish you a quick recovery.

    Thanks.

                string inarg = args[4];
                // Separate directory from file name parts
                string indir = System.IO.Path.GetDirectoryName(inarg);
                string infil = System.IO.Path.GetFileName(inarg);
    
                if (string.IsNullOrEmpty(indir) || !System.IO.Directory.Exists(System.IO.Path.GetDirectoryName(indir)) )
                {
                    // Default to runtime directory where filename but not path provided, or where invalid directory path 
                    // provided.
                    //
                    // At this point you can also go for some other explicitly defined FQP
                    indir = System.IO.Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location);
                }


    • Marked as answer by PrinceLucifer Sunday, February 9, 2020 10:20 AM
    Saturday, February 8, 2020 10:29 PM
  • … sent the reply and just a few seconds later actually realized what the problem is: While the command prompt still shows the path to be C:\Users\Operator\Documents, it seems that StreamWriter expects me to use the localized version (which is C:\Benutzer\Operator\Dokumente in the german UI, as it would show up in Windows Explorer).

    The bummer here is that if I change the Path1 string to @".\Test.txt" and run the program in the Documents folder on a german UI machine, the relative path gets converted to C:\Users\Operator\Documents (at least according to the exception stack) and then fails because in the mind of the German system that directory does not exist. So I do have a good workaround now when passing in full paths, but I still need to figure something out for the relative ones...

    • Marked as answer by PrinceLucifer Saturday, February 8, 2020 10:18 PM
    Saturday, February 8, 2020 8:02 PM

All replies

  • Hi PrinceLucifer,

    Thank you for posting here.

    Maybe your windows explorer hide file extensions of known types.

    Check the following refernces:

    StreamReader complains that file does not exist, but it does

    Hope it can help you.

    Best Regards,

    Xingyu Zhao


    MSDN Community Support
    Please remember to click "Mark as Answer" the responses that resolved your issue, and to click "Unmark as Answer" if not. This can be beneficial to other community members reading this thread. If you have any compliments or complaints to MSDN Support, feel free to contact MSDNFSF@microsoft.com.

    Friday, February 7, 2020 10:54 AM
    Moderator
  • Thanks for the reply. Even if that was true (which I doubt, as that would have to be the same for all directories...) it shouldn't matter. I am opening the file for Create or Append, so the file not existing is a perfectly valid constellation. A file not found should in this case only exists if the directory is write protected, or the directory doesn't exist. Or am I missing something?
    Friday, February 7, 2020 11:39 AM
  • Yes sometimes I get a problem when I overlook something very obvious.

    Have you put a breakpoint on the StreamWriter constructor and then debugged the program and then looked at args[4] to see what is actually there? Alternatively you could show what args[4] is using something such as Console.WriteLine or MessageBox or whatever.



    Sam Hobbs
    SimpleSamples.Info

    Friday, February 7, 2020 8:41 PM
  • Sure did Sam, but didn't even have to. The exception has the filename in it, so I know what it tried to do. So what I did on testing was: 

    1) Run the program from the commandline

    2) Copy the filename (either from the exception or from a console.writeline)

    3) run some other program to write to that file (like notepad + copied filename, or a copy con filename or something like that)

    In all cases the "normal" programs could easily write the file or read it if it was already there, while my StreamWriter would fail. 

    The full exception message, just in case anyone is interested was: 

    "Unbehandelte Ausnahme: System.IO.FileNotFoundException: Die Datei "C:\Users\MyUser\Documents\BAT_Tool\bin\testlog.txt" konnte nicht gefunden werden." 

    (The system obviously is running a german UI pack...) 

    Friday, February 7, 2020 10:19 PM
  • It is a mystery.

    One possibility that was an explanation in a similar question is that perhaps one of the characters is not what you think it is. Copy the full path (directory+filename) to the clipboard from the executing program and try to open that.



    Sam Hobbs
    SimpleSamples.Info

    Friday, February 7, 2020 11:26 PM
  • I am presenting the thing that I get out on a Console.WriteLine. So in C# notation there should be an @ in front of that. I even passed in just "Test.txt" (without the quotation marks), which should lead to using the current directory. It actually does, works fine when I am currently in C:\Temp, does not work when I am in C:\Users\xxx\Documents\MyTool … So I doubt that this has anything to do with the characters I pass in or the way that I escape them. 
    Saturday, February 8, 2020 11:24 AM
  • There is no @ in front of it, because the string never appears in the code. It is passed in as a parameter, as you saw in my original posting. And it seems to be passed in correctly, as it gets displayed correctly in both the Console.WriteLine and the Exception stack. But just so you don't accuse me of trolling I tried adding a string variable and using two different values:

    1) @"C:\Users\xxx\Documents\MyTool\Test.txt" vs. @"C:\Temp\Test.txt" and

    2) @"Test.txt" (which I then called from both directories)

    And once again... Running the program with the temp directory works, running it with the user documents directory doesn't.

    Saturday, February 8, 2020 6:28 PM
  • Sorry that I assumed I had told the whole story, while only passing in parts of the code. This is a standard CLI program, and args is (for me obvious, for others unfortunately not as you pointed out...) the standard definition of the command line arguments. 

    so the relevant code block looks like this:

        class Program
        {
            static StreamWriter Logfile = null;
    
            static int Main(string[] args)
            {
    
                    if (args.Length >= 5)
                    {
                        Logfile = new StreamWriter(args[4], true);
                        Logfile.WriteLine("===========================================================");
                    }
            }
        }
    

    Now here is the test program I mentioned before. Again: Sorry for not sharing all the code, I just assumed that there was enough trust in this community still that I can stay with the relevant parts:

        class Program
        {
            static StreamWriter Writer = null;
            static void Main(string[] args)
            {
                string Path1 = @"C:\Users\Operator\Documents\Test\Test.txt";
                string Path2 = @"C:\Temp\Test.txt";
    
                Console.WriteLine("Testing Path 1: " + Path1);
                try
                {
                    Writer = new StreamWriter(Path1, true);
                    Writer.Write("Test");
                    Writer.Flush();
                    Writer.Close();
                }
                catch(Exception ex)
                {
                    Console.WriteLine(ex.ToString());
                }
                Console.WriteLine("Testing Path 2: " + Path2);
                try
                {
                    Writer = new StreamWriter(Path2, true);
                    Writer.Write("Test");
                    Writer.Flush();
                    Writer.Close();
                }
                catch (Exception ex)
                {
                    Console.WriteLine(ex.ToString());
                }
                Console.WriteLine("Done");
            }
        }
    

    Now funny enough, while testing this some more I seem to have found a hint on the real culprit: If I run the code in a machine that runs on the English UI settings the program works fine. If I run the exact same code on the exact same machine, but switch the UI to German it fails. So I still don't understand what my problem really is, but unless the UI language has anything to do with how strings are interpreted in C#, I guess that it had nothing to do with my strings in the first place.

    And sorry Andrew, I really had no intention of trolling anyone around here. I have been with this community for a long time, and have provided my fair share of answers in my field of expertise as well, but this field just happens to not be C#/.Net. And in my area (which is SQL Server) it is normal to only present pieces of the whole and run of that, so I just did the same here.

    Saturday, February 8, 2020 7:56 PM
  • … sent the reply and just a few seconds later actually realized what the problem is: While the command prompt still shows the path to be C:\Users\Operator\Documents, it seems that StreamWriter expects me to use the localized version (which is C:\Benutzer\Operator\Dokumente in the german UI, as it would show up in Windows Explorer).

    The bummer here is that if I change the Path1 string to @".\Test.txt" and run the program in the Documents folder on a german UI machine, the relative path gets converted to C:\Users\Operator\Documents (at least according to the exception stack) and then fails because in the mind of the German system that directory does not exist. So I do have a good workaround now when passing in full paths, but I still need to figure something out for the relative ones...

    • Marked as answer by PrinceLucifer Saturday, February 8, 2020 10:18 PM
    Saturday, February 8, 2020 8:02 PM
  • Again: Sorry for not sharing all the code, I just assumed that there was enough trust in this community still that I can stay with the relevant parts:

    It honestly has nothing to do with trust.  It has everything to do with the problem programmers actually having and the problem programmers think they're having being mutually exclusive in 99.9% of forum posts.  Always post the code.

    It only became a trust issue because my face hurts and your remark seemed intentionally hurtful.  I'm sorry for my kneejerk.

    Saturday, February 8, 2020 10:11 PM
  • There are three machines involved in my testing, the "real" machine that the problem came from used the user name MarKne (no spaces), the test VM I normally used really has xxx as the user name, so there was no redaction there ;-), and the last test code was run on my developer machine, so Operator is also the actual user name. 

    If I would want to go specifically for those folders I would go through the Environment object anyway, the problem unfortunately started because one of the end users just dropped the program in his Documents folder and run it from there without specifying any paths. So args[4] was really just "Logfile.txt" (without the quotation marks) and was converted to <CurrentDirectory>\Logfile.txt. So even if I parsed for Documents or anything else, I wouldn't have found that.

    Well, now I at least have a stable workaround, so that's good enough. 

    I really appreciate your help Andrew, as well as the thoughts of everyone who stepped in. And I wish you a quick recovery.

    Saturday, February 8, 2020 10:16 PM
  • If I would want to go specifically for those folders I would go through the Environment object anyway, the problem unfortunately started because one of the end users just dropped the program in his Documents folder and run it from there without specifying any paths. So args[4] was really just "Logfile.txt" (without the quotation marks) and was converted to <CurrentDirectory>\Logfile.txt. So even if I parsed for Documents or anything else, I wouldn't have found that.

    Well, now I at least have a stable workaround, so that's good enough. 

    I really appreciate your help Andrew, as well as the thoughts of everyone who stepped in. And I wish you a quick recovery.

    Thanks.

                string inarg = args[4];
                // Separate directory from file name parts
                string indir = System.IO.Path.GetDirectoryName(inarg);
                string infil = System.IO.Path.GetFileName(inarg);
    
                if (string.IsNullOrEmpty(indir) || !System.IO.Directory.Exists(System.IO.Path.GetDirectoryName(indir)) )
                {
                    // Default to runtime directory where filename but not path provided, or where invalid directory path 
                    // provided.
                    //
                    // At this point you can also go for some other explicitly defined FQP
                    indir = System.IO.Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location);
                }


    • Marked as answer by PrinceLucifer Sunday, February 9, 2020 10:20 AM
    Saturday, February 8, 2020 10:29 PM
  • Nice one, thanks again Andrew
    Sunday, February 9, 2020 10:20 AM
  • Nice one, thanks again Andrew

    Did that last post actually work for you?  It should take care of the issue described by your previous post (user put exe in same directory, arguments without path at all), and it should also take care of the Localization issue before that by defaulting to the application-runtime directory where VS sees a different directory structure (through Localization) from what's in the actual filesystem via CLI.

    If doing those steps and then rebuilding the indir + System.IO.Path.DirectorySeparatorChar + infile to feed into your StreamWriter isn't a 100% fix, then you should unmark it so I can delete the post.  This thread is already terribly confused.

    If this most recent post actually is a 100% fix for you, then please unmark the previous posts (by me) so I can delete those instead.  Just curating the thread.

    Sunday, February 9, 2020 6:26 PM
  • Yes, all tests I did look successful, so that's the actual solution. 

    I would consider that behavior a bug in the framework though... If StreamWriter takes "the liberty" to add a path, it should take the correct one.

    Sunday, February 9, 2020 6:54 PM
  • Yes, all tests I did look successful, so that's the actual solution. 

    I would consider that behavior a bug in the framework though... If StreamWriter takes "the liberty" to add a path, it should take the correct one.

    Honestly I think it's more an issue with your own Localization settings in the application.  I just don't have any experience at all with Localization in that manner.  If/when you have time, I strongly recommend you look into that :-) 
    Sunday, February 9, 2020 7:25 PM
  • I have been looking into those settings for the last day now, and you might be right that this can be tweaked out somewhere. My point was just: It is the StreamWriter constructor that rebuilds the name-without-path into a full-path-filename and then passes it on to some other API to open the file. I would assume that the constructor would be built in a matter that does that in a way that conforms with the needs of that API... After all, it was not my choice that the actual file open uses an API that looks at localization...
    Sunday, February 9, 2020 7:34 PM