i have been looking for a way to update my application for ages, and still haven't found a solution. (Please don't say ClickOnce, it isn't suitable for this app).
Years ago i used to use MCadmin to run a Minecraft server, and i remembered that when it started, sometimes it would just say "Update downloaded, please restart!". I have tried to find out how this was done, so i have been looking in the source code and found some things.
Here is some code that i found:
private void CheckUpdateThread()
{
Program.AddRTLine(Color.Green, "Verifying existence of essential files...\r\n", false);
if (!File.Exists("ICSharpCode.SharpZipLib.dll"))
Util.DownloadURLToFile("https://internal.mcadmin.eu/ICSharpCode.SharpZipLib.dll", "ICSharpCode.SharpZipLib.dll");
if (!File.Exists("LICENSE.txt"))
Util.DownloadURLToFile("https://internal.mcadmin.eu/LICENSE.txt", "LICENSE.txt");
Program.AddRTLine(Color.Green, "Essential file validation completed!\r\n", false);
if (Program.dontUpdate)
{
Program.AddRTLine(Color.Green, "Update checking disabled!!!\r\n", false);
return;
}
UpdateRunning = true;
Program.AddRTLine(Color.Green, "Checking for updates...\r\n", false);
bool isUpdate;
if (Program.dontUpdateMCAdmin || 1 == 1)
{
Program.AddRTLine(Color.Green, "MCAdmin update checking disabled.\r\n", false);
}
else
{
isUpdate = Util.DownloadURLToAndDiff("https://internal.mcadmin.eu/MCAdmin.exe", "MCAdmin.exe.new", "MCAdmin.exe");
if (!isUpdate)
{
if (OutOfDateMCA)
{
Program.AddRTLine(Color.Orange, "MCAdmin update downloaded! Restart MCAdmin to apply update!\r\n", false);
SendAdminMessage("MCAdmin update downloaded, consider restarting.", 4);
}
else
{
Program.AddRTLine(Color.Green, "MCAdmin already up to date!\r\n", false);
}
}
else
{
try
{
if (File.Exists("MCAdmin.exe.old"))
File.Delete("MCAdmin.exe.old");
}
catch { }
try
{
if (File.Exists("MCAdmin.exe"))
File.Delete("MCAdmin.exe");
}
catch { }
if (File.Exists("MCAdmin.exe"))
File.Move("MCAdmin.exe", "MCAdmin.exe.old");
File.Move("MCAdmin.exe.new", "MCAdmin.exe");
OutOfDateMCA = true;
Program.AddRTLine(Color.Orange, "MCAdmin update downloaded! Restart MCAdmin to apply update!\r\n", false);
SendAdminMessage("MCAdmin update downloaded, consider restarting.", 4);
}
}
This code is from a single void in a class called "UpdateManager".
See how it does the whole "MCadmin.exe.old" and "MCadmin.exe.new" files, a bit like shadow copying.
There is more to the updater code, but i don't quite understand.
Here is the SVN:
https://code.google.com/p/mcadminfork/source/browse/
Could anybody help me find out how this updater was acheived?
Thanks.
Util.DownloadURLToAndDiff() does the actual downloading and file comparison. So you probably want to look at that.
Otherwise, it's pretty simple:
Download MCAdmin.exe.new
Delete MCAdmin.exe.old (leftover from previous update)
Try to delete current MCAdmin.exe
If delete fails (file in use probably), rename MCAdmin.exe MCAdmin.exe.old
Rename MCAdmin.exe.new MCAdmin.exe
Related
I have an app that reads from text files to determine which reports should be generated. It works as it should most of the time, but once in awhile, the program deletes one of the text files it reads from/writes to. Then an exception is thrown ("Could not find file") and progress ceases.
Here is some pertinent code.
First, reading from the file:
List<String> delPerfRecords = ReadFileContents(DelPerfFile);
. . .
private static List<String> ReadFileContents(string fileName)
{
List<String> fileContents = new List<string>();
try
{
fileContents = File.ReadAllLines(fileName).ToList();
}
catch (Exception ex)
{
RoboReporterConstsAndUtils.HandleException(ex);
}
return fileContents;
}
Then, writing to the file -- it marks the record/line in that file as having been processed, so that the same report is not re-generated the next time the file is examined:
MarkAsProcessed(DelPerfFile, qrRecord);
. . .
private static void MarkAsProcessed(string fileToUpdate, string
qrRecord)
{
try
{
var fileContents = File.ReadAllLines(fileToUpdate).ToList();
for (int i = 0; i < fileContents.Count; i++)
{
if (fileContents[i] == qrRecord)
{
fileContents[i] = string.Format("{0}{1} {2}"
qrRecord, RoboReporterConstsAndUtils.COMPLETED_FLAG, DateTime.Now);
}
}
// Will this automatically overwrite the existing?
File.Delete(fileToUpdate);
File.WriteAllLines(fileToUpdate, fileContents);
}
catch (Exception ex)
{
RoboReporterConstsAndUtils.HandleException(ex);
}
}
So I do delete the file, but immediately replace it:
File.Delete(fileToUpdate);
File.WriteAllLines(fileToUpdate, fileContents);
The files being read have contents such as this:
Opas,20170110,20161127,20161231-COMPLETED 1/10/2017 12:33:27 AM
Opas,20170209,20170101,20170128-COMPLETED 2/9/2017 11:26:04 AM
Opas,20170309,20170129,20170225-COMPLETED
Opas,20170409,20170226,20170401
If "-COMPLETED" appears at the end of the record/row/line, it is ignored - will not be processed.
Also, if the second element (at index 1) is a date in the future, it will not be processed (yet).
So, for these examples shown above, the first three have already been done, and will be subsequently ignored. The fourth one will not be acted on until on or after April 9th, 2017 (at which time the data within the data range of the last two dates will be retrieved).
Why is the file sometimes deleted? What can I do to prevent it from ever happening?
If helpful, in more context, the logic is like so:
internal static string GenerateAndSaveDelPerfReports()
{
string allUnitsProcessed = String.Empty;
bool success = false;
try
{
List<String> delPerfRecords = ReadFileContents(DelPerfFile);
List<QueuedReports> qrList = new List<QueuedReports>();
foreach (string qrRecord in delPerfRecords)
{
var qr = ConvertCRVRecordToQueuedReport(qrRecord);
// Rows that have already been processed return null
if (null == qr) continue;
// If the report has not yet been run, and it is due, add i
to the list
if (qr.DateToGenerate <= DateTime.Today)
{
var unit = qr.Unit;
qrList.Add(qr);
MarkAsProcessed(DelPerfFile, qrRecord);
if (String.IsNullOrWhiteSpace(allUnitsProcessed))
{
allUnitsProcessed = unit;
}
else if (!allUnitsProcessed.Contains(unit))
{
allUnitsProcessed = allUnitsProcessed + " and "
unit;
}
}
}
foreach (QueuedReports qrs in qrList)
{
GenerateAndSaveDelPerfReport(qrs);
success = true;
}
}
catch
{
success = false;
}
if (success)
{
return String.Format("Delivery Performance report[s] generate
for {0} by RoboReporter2017", allUnitsProcessed);
}
return String.Empty;
}
How can I ironclad this code to prevent the files from being periodically trashed?
UPDATE
I can't really test this, because the problem occurs so infrequently, but I wonder if adding a "pause" between the File.Delete() and the File.WriteAllLines() would solve the problem?
UPDATE 2
I'm not absolutely sure what the answer to my question is, so I won't add this as an answer, but my guess is that the File.Delete() and File.WriteAllLines() were occurring too close together and so the delete was sometimes occurring on both the old and the new copy of the file.
If so, a pause between the two calls may have solved the problem 99.42% of the time, but from what I found here, it seems the File.Delete() is redundant/superfluous anyway, and so I tested with the File.Delete() commented out, and it worked fine; so, I'm just doing without that occasionally problematic call now. I expect that to solve the issue.
// Will this automatically overwrite the existing?
File.Delete(fileToUpdate);
File.WriteAllLines(fileToUpdate, fileContents);
I would simply add an extra parameter to WriteAllLines() (which could default to false) to tell the function to open the file in overwrite mode, and not call File.Delete() at all then.
Do you currently check the return value of the file open?
Update: ok, it looks like WriteAllLines() is a .Net Framework function and therefore cannot be changed, so I deleted this answer. However now this shows up in the comments, as a proposed solution on another forum:
"just use something like File.WriteAllText where if the file exists,
the data is just overwritten, if the file does not exist it will be
created."
And this was exactly what I meant (while thinking WriteAllLines() was a user defined function), because I've had similar problems in the past.
So, a solution like that could solve some tricky problems (instead of deleting/fast reopening, just overwriting the file) - also less work for the OS, and possibly less file/disk fragmentation.
I want ask an easy question about my code in c# .... I know that there are lot of topics with same or similar topic/code result. But I need to hand in my code to school, so I can't just use the best solution on Stackoverflow or another page. I showed my code to my teacher and now need to fix a little bug.
The Code is about backing up files with a console report, so in first step I check if a folder exists. Second step is to report that the folder exists or doesn't exist, if it doesn't the code creates this folder and rechecks ...
SITUATION : CONSOLE REPORT
folders doesnt exist:
02:02:06 directory for backup Exist ... can continue
02:02:05 directory for backup DOESNT EXIST ... creating required folders...
folders exist :
02:02:55 directory for backup Exist ... can continue
02:02:54 directory for backup Exist ... can continue
In the 1st example the report is OK, but in the 2nd, my code tells me the same information twice... i just can't get my code to work properly..
Here is my code:
public void checkbackupfolders() {
do {
create_backup_folders();
} while (create_backup_folders() == false);
}
public bool create_backup_folders()
{
string path = "\\BACKUP\\" + Globals.hostname;
if (Directory.Exists(path))
{
consolecho("directory for backup Exist ... can continue");
return true;
}
else
{
consolecho("directory for backup DOESNT EXIST ... creating required folders...");
Directory.CreateDirectory("\\BACKUP\\" + Globals.hostname);
return false;
}
}
Why are you calling the method twice here?:
do {
create_backup_folders();
} while (create_backup_folders() == false);
That's going to make things confusing, as you're now discovering. Just call the method once on each loop iteration and store the result of the method. Then use that stored result in the loop condition:
var canContinue = false;
do {
canContinue = create_backup_folders();
} while (canContinue == false);
Okay, so me and several others are trying to move a bunch of files from a game launcher. To said directory of your choice.
The problem is, the files wont move.
The way the launcher works, is you click install on the game, it installs a bunch of files to the location of your choice. But the files wont move.
Here' the code.
private void MoveFolders()
{
string sourceDir = Config.GetGamePath();
string destinationDir = textBoxFolder.Text;
try
{
if (Directory.Exists(sourceDir) == true)
{
if (bGameIsInstalled == true && textBoxFolder.TextLength > 0)
{
Directory.Move(sourceDir, destinationDir);
bMoveFolders = true;
}
else
{
MessageBox.Show("Select Arma 3 directory before starting game");
}
}
else
{
// Do somthing about source directory not existing -
}
}
catch (Exception ex)
{
//TODO: Handle the execption that has been thrown will do this on launcher update
}
}
You can use CopyFile. As you said this should be a installer, i wouldn't move them folders to another direction. Just copy it, because you can't use the installer one more time after all these files needed are moved away.
And if you debug it, please just don't use try and catch. Test your code simply.
I m trying to commit changes to the project to my svn server but it doesnt seem to work.
The Test:
I run the code below with the folder where nothing has been changed and nothing happens as expected, Then I create a new folder and then run the code again, this time nothing seems to happen, the code runs and returns without the error showing it worked.
public bool svnCommitProject(String project, String message)
{
using (SvnClient client = new SvnClient())
{
try
{
SvnCommitArgs args = new SvnCommitArgs();
args.LogMessage = message;
client.Authentication.ForceCredentials(Properties.Settings.Default.Username, Properties.Settings.Default.Password);
return client.Commit(Properties.Settings.Default.LocalFolderPath + Properties.Settings.Default.Username + #"\" + project, args);
}
catch
{
MessageBox.Show("ERROR");
return false;
}
}
}
Suspected Problem:
From looking at this and google i suspect that the problem exists because the file hasnt been "added" to svn control, but im not sure.
Is this the case? and if so how would I go about adding the files which need to be added? I also assume that something similar would be needed for files which are deleted/modified, is this correct and how would I add this in too?
See Find files not added to subversion
Yes, files just dropped into the local working directory doesn't tell the SVN to commit to.
Collection<SvnStatusEventArgs> filesStatuses = new Collection<SvnStatusEventArgs>();
if (!client.GetStatus(localDir, new SvnStatusArgs
{
Depth = SvnDepth.Infinity,
RetrieveRemoteStatus = true,
RetrieveAllEntries = true
}, out workDirFilesStatus))
{
throw new SvnOperationCanceledException("SvnClient.GetStatus doesn't return anything.");
}
filesStatuses.Where(i => i.LocalContentStatus == SvnStatus.NotVersioned).ToList().ForEach(i => svnC.Add(i.Path));
filesStatuses.Where(i => i.LocalContentStatus == SvnStatus.Missing).ToList().ForEach(i => svnC.Delete(i.Path));
I've written a simple windows service to watch a folder and run relog (the windows tool to export data from binary perf mon files) on any files that arrive.
When I run it from my c# process (using System.Diagnostics.Process.Start()) I get:
Error:
Unable to open the specified log file.
But if I copy and paste the command into a console window it works fine.
I've looked all over the net but everything seems to point to a corrupt file, which I know is not the case as I can import perfectly when running manually.
Any help greatly appreciated.
If you are using FileSystemWatcher to monitor for files it will fire the created event before the file is completely written to disk, this would cause the kind of error from relog about being unable to "open" a file since it might still be locked and technically corrupt as far as it's concerned.
I've written the following helper method that I always use in conjunction with FileSystemWatcher to wait for a file to be completely written and ready for processing after a created event and will also kick out after a timeout:
public static bool WaitForFileLock(string path, int timeInSeconds)
{
bool fileReady = false;
int num = 0;
while (!fileReady)
{
if (!File.Exists(path))
{
return false;
}
try
{
using (File.OpenRead(path))
{
fileReady = true;
}
}
catch (Exception)
{
num++;
if (num >= timeInSeconds)
{
fileReady = false;
}
else
{
Thread.Sleep(1000);
}
}
}
return fileReady;
}