I'd like to create an updater that checks if the main .exe file is updated. Must i create an updater file that closes the main application and overwrites the .exe file or is there another way?
Must i create an updater file that closes the main application and overwrites the .exe file or is there another way?
If you want to overwrite the exe then yes you must exit the currently running exe. The way I currently handle this is the following (it is a Winforms app for the record)
1) update check is performed from internet
2) download of update starts to temporary folder (yourapp/temp_update)
3) once download completes, application uses Process.Start to launch updater.exe
4) updater.exe is passed arguments via the command line - namely the exe name and folder location so it can
know which Process to wait for exit (your main application)
where to copy the new exe (and any other files)
eg
var process = new Process();
process.StartInfo.FileName = "updater.exe"
process.StartInfo.Arguments = " yourApp.exe temp_update"
process.Start();
//now exit your app.
Environment.Exit(0); //if Winforms app then just call `.Close()` on your main form
4) main application exits
6) updater.exe calls Process.Start( ) to restart your main application; update.exe then
5) updater.exe detects exit - it can now copy the new exe over the old one exists
Rarely, updater.exe itself needs updating - i embed it inside inside the exe (This is typically ok because update.exe is tiny) - the new exe would then extract it and copy over the existing updater.exe.
I've seen some other possible alternatives using AppDomains whereby your exe is a very lightweight executable that checks for updates and then loads your 'main application' into a new AppDomain : (pseudo code)
public static void Main(string[] args)
{
//dont reference MyApp.dll from this exe!
if(CheckForUpdate())
{
//download new MyApp.dll
}
//load MyApp.dll in a new AppDomain
}
however the problem with these approaches it doesnt matter how lightweight you make that initial exe you may need to update it and then you are back at the original problem!
You must. And it is more complex. Normal users have no right to replace an executable for security reasons. It requires escalated rights as the programs folder is protected.
I would start looking into installer technologies and make a version check before. This is not that hard.
Related
I want to have my C# (Xamarin) program run an EXE or batch (BAT) file. The user will be running my program, and will click on one of several buttons, some of which open Web pages and others of which run external programs. These files will be on the same computer as the one running the main program and don't need greater permissions. The overall program will be in Windows, UWP.
I already have code to pull info from the database saying "the button the user clicked references a program and it's (eg) C:\Tools\MyTool.exe". (Real path more like (C:\Users\Me\source\repos\ProductNameV2\ProductName\ProductName.UWP\Assets\EXE\whatever.exe".) I used a "demo.bat" file containing nothing but echo and pause statements, or references to a built-in Windows program like Notepad or Calc that an ordinary command prompt can recognize without an explicit path (ie. that's part of the recognized system Path). Yes, the real path to the dummy file does exist; I checked. I've also explicitly added files demo.bat and dummy.txt to my C# project.
Here's roughly what I've tried so far to actually run a batch file, or an EXE, or just to try opening a text file. Nothing works.
1)
bool check = await Launcher.CanOpenAsync(#"file:///C:\Tools\demo.bat"); // Returns false.
bool check = await Launcher.CanOpenAsync(#"file:///C:\Tools\dummy.txt"); // Returns true.
await Launcher.OpenAsync(#"file:///C:\Tools\demo.bat") // Seems to do nothing; silently fails.
await Launcher.OpenAsync(#"file:///C:\Tools\dummy.txt") // Same.
2)
Process batchProcess = new Process();
batchProcess.StartInfo.FileName = #"file:///C:\Tools\demo.bat"; // Same result with notepad.exe
batchProcess.StartInfo.WindowStyle = ProcessWindowStyle.Normal;
batchProcess.Start();
batchProcess.WaitForExit();
// Result: "Access is denied" error during Start().
3)
var otherProcessInfo = new ProcessStartInfo(#"file:///C:\Tools\demo.bat")
var otherProcess = Process.Start(otherProcessInfo);
otherProcess.WaitForExit();
otherProcess.Close();
// Result: "The system cannot find the file specified" despite it being the same path as in previous examples.
// Also tried literally using the path C:\Tools\demo.bat, without adding that to the C# project.
// One thing that slightly works is to use:
var otherProcessInfo = new ProcessStartInfo("cmd.exe", "/c echo Hello world!");
// This version opens a window and instantly closes it again. With "/c pause" instead, it opens, saying "press any key to continue".
// Chaining multiple commands with newline or semicolon characters doesn't work as a form of batch file.
So: the only tiny success I've had here is to run cmd.exe, to run a one-line command. I suppose that depending on what the batch file must do, there's some possibility of receiving a string, breaking it into lines, then running cmd.exe using method 3 to call them one at a time. Which is ugly at best.
Is there some better way to do this -- to run a batch file or an EXE from within my program?
EDIT: Yes, I did in fact look at documentation before asking. Why did I use URIs? Because of multiple errors telling me that the simple path strings ("C:\this\that") I was using were in an "Invalid URI format". Using Process.Start("notepad.exe") silently fails, doing nothing. Using a method involving System.Diagnostics.Process (found at How to run external program via a C# program? and yes I saw that before) fails with an error of "Access denied" when using my batch file reference, or silently failing (no window opens) using plain old notepad.exe. I avoided setting Process options that say hide the window.
So to rephrase: Is there a way to make my program run some EXE somewhere on the computer, or to run a batch file that has more than one command in it? What is that way?
Using the data you collected, I was able to run a batch file by doing the following:
var strPathToExeOrBat = System.IO.Path.Combine("C:\\Tools", "demo.bat");
var otherProcessInfo = new ProcessStartInfo("cmd.exe", $"/c call \"{strPathToExeOrBat\"");
var otherProcess = Process.Start(otherProcessInfo);
otherProcess.WaitForExit();
otherProcess.Close();
I also think it would be helpful to review the capabilities of the cmd.exe application.
I found this post to be helpful:
https://stackoverflow.com/questions/515309/what-does-cmd-c-mean#:~:text=%2FC%20Carries%20out%20the%20command%20specified%20by%20the%20string%20and,switches%20by%20typing%20cmd%20%2F%3F%20.
In particular the /k option will leave the window open, if you don't want it to close after running a script.
Thank you very much for your question! It really helped me find the answer to this! (at least for my situation of a .NET MAUI windows app, but MAUI is built off of Xamarin.Forms, so you shouldn't have a problem doing the same thing)
EDIT: Updated to use file path from question and string interpolation with System.IO.Path.Combine for slightly greater cross platform capability
The uninstallers from an application normally launch new process and I am trying to make my application know about it so if after the uninstallation, it will execute some tasks.
NSIS for example.
string uninstallstring = "C:\\Program Files (x86)\\Pidgin\uninstall.exe"; // reg.GetValue("UninstallString").ToString();
Process p = new Process()
{
StartInfo = new ProcessStartInfo()
{
FileName = uninstallstring
},
EnableRaisingEvents = true
};
p.Start();
p.WaitForExit();
Debug.WriteLine(p.HasExited);
Debug.WriteLine(p.ExitCode);
// clean up TASKS after uninstall here
// clean clean clean
code above will try uninstall Pidgin. It will Start() and it should wait for the uninstaller to finish its job and show in Debug panel True/False (HasExited) as well as its ExitCode.
The problem there is, after Start() is executed, the "uninstall.exe" launched a new application "Au_.exe" in "C:\Users\Jayson\AppData\Local\Temp\~nsu.tmp" and "uninstall.exe" closed, WaitForExit() has done its job and Debug panel shows
True
0
those are "HasExited" and "ExitCode" but the Pidgin Uninstall is still running which tells me that "uninstall.exe" launched the "Au_.exe" as not his child process.
My question is, how do I make sure that the uninstaller finish its job?
If you know name of application, started by unnistaller, you can try to check existence of his procces use Process.GetProcessesByName("Au_.exe") by timer. When procees appeared and then disappeared you can raise callback.
Also you can read about GetExitCodeProcess
I think there is no general solution here. You rely on external component that doesn't work as expected (normally we expect that uninstall.exe will exit only after complete uninstall and will return corresponding error code, but it doesn't).
There are several workarounds:
When uninstall.exe complete, look for Au_.exe process and wait for its completion.
After uninstall.exe (and may be Au_.exe) check is it really uninstall Pidgin. You can check filesystem or some special registry keys. Probably you expect something before your custom cleanup tasks. If it was not uninstalled properly - repeat or don't clean up (may be show some error or write log message - it depends on your application).
Made your custom uninstaller for Pidgin that will cleanup all necessary resources. Pidgin is open-source, so you can look how its installer works. May be it will be enough to simply modify existing installer.
Don't worry about Au_.exe and just cleanup your resources - may be it's really not a problem for your application.
(if possible) Ask user of your application to uninstall Pidgin. When user did it - make some simple checks (e.g. C:/Program Files/Pidgin is deleted) and do your cleanup.
I'm trying to create an auto-updating app using Winforms. I can't use click-once or the updater block because the app needs to run on Mono also.
Once I download the new exe file from a webservice, is it possible to unlock the running exe file, replace it, and restart the app?
No. You can't replace a running executable. You'll get an access violation.
The best option is to create a separate, small executable that does the replacement for you. Your application can call it, shut itself down, and the second executable can do the download/replace/relaunch for you.
This also has the advantage of allowing you to setup the "updated" to require elevated permissions, which may be required, depending on where the application you're replacing is installed.
I wouldn't do that (It won't work; you'll get an access violation). Instead you could:
Download another executable to somewhere on the user's pc (Temporary Documents or your app's folder, for example) then let that update your program.
After the update has finished, start up your updated program and pass the location of the updater to it, where your updated app can now delete it. Or just leave the updater where it is.
I have a c# program through which i am opening cmd window as a a process. in this command window i am running a batch file. i am redirecting the output of that batch file commands to a Text File. When i run my application everything seems to be ok.
But few times, Application is giving some error like "Can't access the file. it's being used by another application" at the same time cmd window is not getting closed. If we close the cmd process through the Task Manager, then it's writing the content to the file and getting closed. Even though i closed the cmd process, still file handle is not getting released. so that i am not able to run the application next time onwards.Always it's saying Can't access the file. Only after restarting the system, it's working.
Here is my code:
Process objProcess = new Process();
ProcessStartInfo objProInfo = new ProcessStartInfo();
objProInfo.WindowStyle = ProcessWindowStyle.Maximized;
objProInfo.UseShellExecute = true;
objProInfo.FileName = "Batch file path"
objProInfo.Arguments = "Some Arguments";
if (Directory.Exists(strOutputPath) == false)
{
Directory.CreateDirectory(strOutputPath);
}
objProInfo.CreateNoWindow = false;
objProcess.StartInfo = objProInfo;
objProcess.Start();
objProcess.WaitForExit();
test.bat:
java classname argument > output.txt
Here is my question:
I am not able to trace where the problem is..
How we can see the process which holding handle on ant file.
Is there any suggestions for Java and .net interoperability
In situations like this I start up the Process Explorer ( by Sysinternals, awesome tool btw ) click Ctrl+F, and enter the name of the file. It will search across all running processes and will list the file handles to this file by the applications that have it open.
You can then either drop the handle, or kill the app - whatever you think is better )
You can try forking and attaching file descriptor from C# rather than launching a bat file.
I think the problem is because the java program is accessing the text file when the C# program is writing something on it, and hence a "file cannot access" problem.
If I were you, I would do everything in C#-- I won't use Java to read the state of the C# program. And I would access the file only after I've completed whatever the C# needs to do.
As for to see what process is locking up your file, you can use Process Explorer for this purpose.
How do I programmatically self delete?
C# / .NET Compact Framework 2 / Windows Mobile 6
Please, I don't want to discuss WHY to do it, I just need to know HOW to do it!
Important:
The "second application" approach is NOT an option. (Unless that second application can be "extracted" from running app, but I don't know how to do it!).
No problem in forced reboot, if windows do the trick at startup. (Is it possible? Nice! Show me how!).
Code samples are welcome.
The only way I can think of offhand to delete yourself and leave no trace is to use something already present in the device- namely wceload (the CAB extractor). I'd create a simple CAB file with a custom installer DLL that does a wait and then the delete.
I'd then add the CAB to the app as an embedded resource. When you need to delete you
extract the CAB to the file system
execute wceload.exe with the CAB as a parameter and /noui (or /silent)
Quit your application
The CAB then deletes your file (a named mutex could sync this better than just a Sleep call in the DLL). wceload automatically deletes the CAB (well depending on WinMo version, but there is a switch to force delete if necessary).
It's certainly a hack, but it would provide a "leave no trace" delete. Of course the CAB would probably have to clean it's own installation registry entries as well. Maybe you could just have the install return "failure" to prevent them from being written in the first place.
I am using this code and it works fine
string AppPath = System.IO.Path.GetDirectoryName(Assembly.GetEntryAssembly().Location).ToString() + "\\Uninstaller.exe";
ProcessStartInfo Info = new ProcessStartInfo();
Info.Arguments = "/C choice /C Y /N /D Y /T 0 & Del " + AppPath;
Info.WindowStyle = ProcessWindowStyle.Hidden;
Info.CreateNoWindow = true;
Info.FileName = "cmd.exe";
Process.Start(Info);
I've done this in the past by simply writing a batch file to the file system that will wait a few seconds and then delete the program. You then use Process.Start() to kick off the batch file and immediately call Environment.Exit(). You need to make sure that the batch file waits long enough for your program to close, or it won't work.
Windows can delete files on startup. It can be done by calling MoveFileEx like:
MoveFileEx(szDstFile, NULL, MOVEFILE_DELAY_UNTIL_REBOOT);
I'm not sure if that API is available in Mobile 6 or not. However, what it actually does is create a registry entry in HKEY_LOCAL_MACHINE\System\CurrentControlSet\Control\Session Manager\PendingFileRenameOperations. The key is a REG_MULI_SZ and you just need to give it a value like "szDstFile\0\0". On reboot Windows deletes the file.
As for programmatically rebooting, have a look at this thread on SO.