Can't understand C# windows phone 8.1 (SL) file storage - c#

I'm writing a simple quizz game for WP8.1, at some point I decided to add a highscore capability.
I took an option which I know is not the best one but that should work anyhow : include a highscore.txt file in my application with a default value 0 and placed this file in the "Resources" folder. When the actual score is higher than the highscore I want to overwrite.
Here is how I have proceeded :
First: a method to grab the highscore and store it in a variable (This first method works pretty fine, it reads the file and gets the highscore)
public async void getHighScore(){
Uri highScoreFileLoc = new Uri("ms-appx:///Resources/Highscore.txt");
StorageFile file = await StorageFile.GetFileFromApplicationUriAsync(highScoreFileLoc);
string highScoreSTR = await FileIO.ReadTextAsync(file);
}
A method to write the highscore into the file. My thought is that if I use the same exact file path and name, I should overwrite the file I have just opened.
private async void ecritHighScore(int highScore)
{
byte[] line = System.Text.Encoding.UTF8.GetBytes(scoreJoueur.ToString());
Uri HighScoreFileLoc = new Uri("ms-appx:///Resources/Highscore.txt");
StorageFile file2 = await StorageFile.GetFileFromApplicationUriAsync(HighScoreFileLoc);
string highScoreSTR = highScore.ToString();
await FileIO.WriteBytesAsync(file2, line);
}
When I run the app, the highscore is written and read correctly the second time.
But if I quit the emulator and run it again, the highscore is back to the default value: the original Highscore.txt file was not overwritten...
To sum up the situation :
I open a file and read it correctly but when I write into the same exact file (Uri is exactly the same), it is another file that is written into.
How is this possible ? It seems to me that I am missing something somewhere, but I could not find out...
Any help would be much appreciated.

You file uri points to the install directory which is read-only. (And your first write will probably fail on a real device.
Use an ms-appdata:// instead, which is the apps local directory.

Related

I think I am doing this wrong, won't copy to relative path

public void Save_Token(string _Token)
{
var Token_Location = #".\token.txt";
using (StreamWriter sw = new StreamWriter(Token_Location))
{
sw.WriteLine(_Token);
}
}
I tried to get the token from the api (json) and I deserialized and saved it. I would like to write to the file to save for later. But I want this application to be ran on anyone's PC. So I don't want to use the full path.
I also tried
Path.Combine(Environment.CurrentDirectory,Token_Location);
still nothing is written, unless I use the full path.
You can't guarantee that the current user has write access to the folder from where the file is executed. There is a special folder (APP_DATA) that applications are supposed to use when storing user data on a computer:
public void Save_Token(string _Token)
{
var tokenDirectory = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "YourCompanyOrOrganizationName");
var tokenFile = Path.Combine(tokenDirectory, "token.txt");
Directory.CreateDirectory(tokenDirectory);
File.WriteAllText(tokenFile, _Token);
}
Your file will then be stored in a path like "C:\Users\yourusername\AppData\Roaming\YourCompanyOrOrganizationName\token.txt"
It is generally a bad idea to use a relative path in software source code because the "current working directory" of the process that the relative path is relative to can change over the runtime of the application.
Activities like showing a file open dialog or using a third-party component can unexpectedly change the current working directory so that it is dangerous to assume a certain current working directory.

Why is the absolute path used to read text files in C #, and the path will change

I use unity3d and vs2019 to read a text file (txt) as follows:
public class MeshTest01 : MonoBehaviour
{
// Start is called before the first frame update
void Start()
{
string filePath = #"‪‪‪D:\Desktop\Unity\Mesh\77.txt";
string[] lines = File.ReadAllLines(filePath);
foreach (var item in lines)
{
Debug.Log(item);
}
//List<string> planesStr = TxtOperation.GetFaces2str(linesStr);
//Debug.Log(TxtOperation.GetFaceIndex(planesStr[0]));
}
}
The error information is as follows:
DirectoryNotFoundException: Could not find a part of the path "E:\project\GitHub\RobotSimulation\***D:\Desktop\Unity\Mesh\77.txt".
Why does the previous part of the file path appear
Honestly, I can't explain why this happens.
Usually yes, this would be one way of how to read an external file.
My guess would be that since your file is placed on a different drive the project might be sandboxed and therefore interprets the path as a relative one. Never tried to load files from different drives to be honest.
However, in general I would keep the files together with the project they belong to and place them
either in the Application.streamingAssetsPath in the editor in the folder Assets/StreamingAssets for files that shall be
read/write able for the editor
but readonly later in a build
or the Application.persistentDataPath for builds if you want to
write to them via code
or let the user change the content
externally afterwards
Then you would get your path using
var path = Path.Combine(Application.streamingAssetsPath, "SomeFolder", "77.txt");
or accordingly
var path = Path.Combine(Application.persistentDataPath, "SomeFolder", "77.txt");
For the streaming assets there are some special cases (e.g. on Android) where you need to read the file using UnityWebRequest.Get since it gets compressed.
Alternatively if it is an option for you you could also directly drag it into the Assets (or any folder below except special ones like StreamingAssets, Resources etc) and then directly drag it into a field via the Inspector using it as a (read-only) TextAsset
[SerializeField] private TextAsset meshFile;
and later access it's content via
var lines = meshFile.text.Split('/n');
Also I general: You should make a huge circle around the Resources!
Unity themselves strongly recommend to not use it! This and the reasons can be found in the Best practices.

Check inside loop if *txt file has been created

My code is searchcing inside a loop if a *txt file has been created.
If file will not be created after x time then i will throw an exception.
Here is my code:
var AnswerFile = #"C:\myFile.txt";
for (int i = 0; i <= 30; i++)
{
if (File.Exists(AnswerFile))
break;
await Task.Delay(100);
}
if (File.Exists(AnswerFile))
{
}
else
{
}
After the loop i check my file if has been created or not. Loop will expire in 3 seconds, 100ms * 30times.
My code is working, i am just looking for the performance and quality of my code. Is there any better approach than mine? Example should i use FileInfo class instead this?
var fi1 = new FileInfo(AnswerFile);
if(fi1.Exists)
{
}
Or should i use filewatcher Class?
You should perhaps use a FileSystemWatcher for this and decouple the process of creating the file from the process of reacting to its presence. If the file must be generated in a certain time because it has some expiry time then you could make the expiry datetime part of the file name so that if it appears after that time you know it's expired. A note of caution with the FileSystemWatcher - it can sometimes miss something (the fine manual says that events can be missed if large numbers are generated in a short time)
In the past I've used this for watching for files being uploaded via ftp. As soon as the notification of file created appears I put the file into a list and check it periodically to see if it is still growing - you can either look at the filesystem watcher lastwritetime event for this or directly check the size of the file now vs some time ago etc - in either approach it's probably easiest to use a dictionary to track the file and the previous size/most recent lastwritedate event.
After a minute of no growth I consider the file uploaded completely and I process it. It might be wise for you to implement a similar delay if using a file system watcher and the files are arriving by some slow generating method
Why you don't retrieve a list of files name, then search in the list? You can use Directory.GetFiles to get the files list inside a directory then search in this list.
This would be more fixable for you since you will create the list once, and reuse it across the application, instead of calling File.Exists for each file.
Example :
var path = #"C:\folder\"; // set the folder path, which contains all answers files
var ext = "*.txt"; // set the file extension.
// GET filename list (bare name) and make them all lowercase.
var files = Directory.GetFiles(path, ext).Select(x=> x.Substring(path.Length, (x.Length - path.Length) - ext.Length + 1 ).Trim().ToLower()).ToList();
// Search for this filename
var search = "myFile";
// Check
if(files.Contains(search.ToLower()))
{
Console.WriteLine($"File : {search} is already existed.");
}
else
{
Console.WriteLine($"File : {search} is not found.");
}

Program doesn't read text file on startup, but it does when I start it?

So as the question says I have a program set up to start at windows startup, but it doesn't read the .txt next to it, when I launch the program it does.
bool cdexist = File.Exists("cd.txt");
if (cdexist)
{
StreamReader sr = new StreamReader("cd.txt");
time = Convert.ToInt32(sr.ReadLine());
sr.Close();
if (time != 0)
{}.....rest of the code
Whats could be the issue?
Edit: part where I write to the file
{
timeleft = time - i;
label1.Text = timeleft.ToString();
StreamWriter sw = new StreamWriter("cd.txt");
sw.Write(timeleft);
sw.Close();
i++;
}
EDIT
I am unable to solve the issue, I tried recreating what happens when the PC starts, and as soon as I'm at the desktop I start the program and it's working, but when the system does it automatically with windows it doesn't seem to read the .txt.
File paths like "cd.txt" are always interpreted relatively. And usually they are interpreted as relative to the current working directory. When you just execute your program e.g. from your Windows Explorer, then the working directory is the executable’s location. So it will look for the file directly next to the application.
However, when executed in a different fashion, it’s likely that the working directory is very different. So if you expect the file to be located next to the executable, you should change your program to look for it there. For example like this:
string filePath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "cd.txt")
bool cdexist = File.Exists(filePath);
// …
Check this question for other ways to get the correct directory.

Deploy an application's xml file with installer or create it on the fly if it does not exist

I am having an xml file like:
<CurrentProject>
// Elements like
// last opened project file to reopen it when app starts
// and more global project independend settings
</CurrentProject>
Now I asked myself wether I should deliver this xml file with above empty elements with the installer for my app or should I create this file on the fly on application start if it does not exist else read the values from it.
Consider also that the user could delete this file and that should my application not prevent from working anymore.
What is better and why?
UPDATE:
What I did felt ok for me so I post my code here :) It just creates the xml + structure on the fly with some security checks...
public ProjectService(IProjectDataProvider provider)
{
_provider = provider;
string applicationPath = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData);
_projectPath = Path.Combine(applicationPath,#"TBM\Settings.XML");
if (!File.Exists(_projectPath))
{
string dirPath = Path.Combine(applicationPath, #"TBM");
if (!Directory.Exists(dirPath))
Directory.CreateDirectory(dirPath);
using (var stream = File.Create(_projectPath))
{
XElement projectElement = new XElement("Project");
projectElement.Add(new XElement("DatabasePath"));
projectElement.Save(stream, SaveOptions.DisableFormatting);
}
}
}
In a similar scenario, I recently went for creating the initial file on the fly. The main reason I chose this was the fact that I wasn't depending on this file being there and being valid. As this was a file that's often read from/written to, there's a chance that it could get corrupted (e.g. if the power is lost while the file is being written).
In my code I attempted to open this file for reading and then read the data. If anywhere during these steps I encountered an error, I simply recreated the file with default values and displayed a corresponding message to the user.

Categories