I'm using Visual Studio 2015, C#, WPF.
I'm trying to give MyProgram.exe the ability to easily update itself.
I have code that downloads the file MyProgram.7z to %UserProfile%\AppData\Local\Temp\.
From the 7z it extracts MyProgram.exe to the currently running MyProgram.exe's directory.
While the 7-Zip extraction is starting, the program closes itself to allow overwriting the exe.
// AppData Temp Directory
string tempDir = System.IO.Path.GetTempPath();
// MyProgram.exe Current Directory
string currentDir = Directory.GetCurrentDirectory().TrimEnd('\\') + #"\";
// ...
//
// Download Code Here
// Saves MyProgram.7z to AppData Temp
//
// ...
// Unzip MyProgram.7z
// Overwrite MyProgram.exe
//
using (Process extract = new Process())
{
extract.StartInfo.UseShellExecute = false;
extract.StartInfo.CreateNoWindow = false;
extract.StartInfo.RedirectStandardOutput = true;
extract.StartInfo.FileName = "7z.exe";
extract.StartInfo.Arguments = "-r -y e " + "\"" + tempDir + "MyProgram.7z" + "\"" + " -o\"" + currentDir + "\"" + " *";
// 7z.exe -r -y e "C:\Users\Matt\AppData\Local\Temp\MyProgram.7z" -o"C:\Program Files\MyProgram\" *
extract.Start();
}
// Exit Program
// 7-Zip will continue to run in the background after MyProgram.exe has exited
//
Environment.Exit(0);
I've tested it and it appears to work.
Is there a better way of doing this? Is there anything that can go wrong with this way? I'like to have it within the program and not in a separate helper program. I also need to use 7z.exe in this case.
Is there a way to instead launch cmd and chain commands into it like timeout 5 && 7z.exe ... and have it relaunch the program after extraction?
You may want to check out Squirrel. It is an open-source library that completely manages both installation and updates of desktop Windows applications.
There is a getting starting guide available on GitHub: https://github.com/Squirrel/Squirrel.Windows/blob/master/docs/getting-started/0-overview.md.
There is also the ClickOnce deployment technology from Microsoft: https://msdn.microsoft.com/en-us/library/142dbbz4(v=vs.90).aspx.
No reason to reinvent the wheel.
Instead of launching 7z process directly you can also:
launch cmd /c process that will wait 5 seconds, then unzip, the relaunch the app
shutdown yourself
cmd /c timeout 5 & [your unzip command] & MyProgram.exe
PS. & is used to execute multiple commands one after another
Related
So been searching or the web but can't seem to find an answer that has helped me. I have been looking for almost a week now.
I created a program in vs, alongside with some batch files. The Batch files run great by themselves and through the debug/release when including them in the folder with the .exe.
My problem is I want to be able to ONLY have the .exe file from my release and it still work.
Is there a way i can build these files inside the .exe? I have tried using c# to write my console commands instead of including seperate batch files. But im pretty new to c# and i get nothing but errors with the commands i want to run/if i run to many lines.
I would much rather have just c# instead of including the batch files but that I can't seem to figure out a solution to either.
Any help would be appreciated.
This is me currently calling batch files which works just fine. Again, if there is a way to just write this in c# instead of calling a batch file I would be happy to learn.
Process process = new Process();
ProcessStartInfo psi = new ProcessStartInfo();
psi.CreateNoWindow = false;
psi.Verb = "runas";
psi.FileName = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location) + #"/" + "undo.bat";
psi.UseShellExecute = true;
process.StartInfo = psi;
_ = process.Start();
process.WaitForExit();
I'm starting CyberSecurity soon and am playing around with some Security stuff on my computer. Below is a sample code from my batch file to enable Security Protocols. If anything how would i write this in c#?
echo ...
echo Enabling Windows Firewall
netsh advfirewall set allprofiles state on
echo Enalbing HyperVisor
bcdedit /set hypervisorlaunchtype auto
echo Enabling UAC
%windir%\System32\reg.exe ADD HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System /v EnableLUA /t REG_DWORD /d 1 /f
echo.
echo.
echo Your Computer will now be restarting for changes to take effect!
timeout 10
shutdown /r /t 001
What you can do is include the batchfiles as embedded resources in your project. Then read them and then execute them.
to include them as embedded resources example...
add them to your project.
right click and go to properties
select embedded resource
then to extract...
Write file from assembly resource stream to disk
you can then write the file to disk and create process on it. or there is a way to execute cmd.exe without writing the file to disk but this is a little complicated so the best way is to just write to disk.
Execute BATCH script in a programs memory
I followed the guide given above and a few others to get my solution to work. Embed the resource that's in your solution, then I used the following code to pretty much create the functions of being able to write it.
private static void Extract(string nameSpace, string outDirectory, string internalFilePath, string resourceName)
{
Assembly assembly = Assembly.GetCallingAssembly();
using (Stream s = assembly.GetManifestResourceStream(nameSpace + "." + (internalFilePath == "" ? "" : internalFilePath + ".") + resourceName))
using (BinaryReader r = new BinaryReader(s))
using (FileStream fs = new FileStream(outDirectory + "//" + resourceName, FileMode.OpenOrCreate))
using (BinaryWriter w = new BinaryWriter(fs))
w.Write(r.ReadBytes((int)s.Length));
}
Here is what I used to save, execute then delete the file.
Extract("nameSpace", "outDirectory", "internalFilePath", "resourceName");
Process process = new Process();
ProcessStartInfo psi = new ProcessStartInfo();
psi.CreateNoWindow = false;
psi.Verb = "runas";
psi.FileName = #"C:/" + "resourceName";
psi.UseShellExecute = true;
process.StartInfo = psi;
_ = process.Start();
process.WaitForExit();
System.Threading.Thread.Sleep(10);
if ((System.IO.File.Exists(psi.FileName)))
{
System.IO.File.Delete(psi.FileName);
}
Keep in mind im new when it comes to this so im sure there is a better way of writing it, but this worked for me!
So i want to run a bunch of different batch commands that do different things. Currently my software works by running the .bat file that is included with the software, but i want to simply remove the syntax from the .bat file and integrate it inside my software so when i click the button it runs the code directly and not launch the .bat file.
Problem is how do i run multiple batch commands, not only that but also wait for each command to finish running just like batch does?
Overall i wanna execute batch commands like stop services, delete registry files, delete folders and software.
PS : this is a C# Forms application ( so i click one button and it all executes )
Currently i have : with examples
var proc1 = new ProcessStartInfo();
string stopservices =
"net stop \"serviceexample1\"" +
"net stop \"serviceexample2\"";
string deleteregistry =
"REG ADD HKEY_LOCAL_MACHINE\\SYSTEM\\EXAMPLE\\.. / t REG_DWORD / v Start / d 0x00000004 / f" +
"rem REG ADD HKEY_LOCAL_MACHINE\\SYSTEM\\EXAMPLE\\.. /t REG_SZ /v Start /d 5 /f";
string deletesoftware =
"MsiExec.exe /X{EXAMPLESOFTWARE} /quiet" +
"MsiExec.exe /X{EXAMPLESOFTWARE} /quiet";
string removedirectories =
"rmdir /s /q C:\\Program Files\\EXAMPLE" +
"rmdir /s /q C:\\Program Files(x86)\\EXAMPLE";
proc1.UseShellExecute = true;
proc1.WorkingDirectory = #"C:\Windows\System32";
proc1.FileName = #"C:\Windows\System32\cmd.exe";
proc1.Verb = "runas";
proc1.Arguments = "/c " + stopservices + deleteregistry + deletesoftware + removedirectories;
proc1.WindowStyle = ProcessWindowStyle.Normal;
System.Diagnostics.Process processwait = System.Diagnostics.Process.Start(proc1);
while (!processwait.HasExited && processwait.Responding)
{
System.Threading.Thread.Sleep(100);
}
I have tried to look in internet how to run batch code directly from c# and the only way i found how to do it was using a string. But i dont think that will wait for each command to finish to begin the next one because a string is just a line of text so it will take it all at once.
I also wanna make sure that when the code was successfully executed the command prompt will stay untill i close it ( like pause in batch ).
Sorry if this is a simple question, i am a student with less then half a year experience in c#
I wanted to create a text file with all names of the files in a folder.
So I went like this:
Process getNamesProcess = new Process();
getNamesProcess.StartInfo.FileName = #"c:\**my folder path**";
getNamesProcess.StartInfo.Arguments = "dir /b >> names.txt";
getNamesProcess.Start();
However this doesn't work for me. The process opens me a folder, nothing more. What should I change?
I do not know what you were hoping to accomplish by placing a folder path in StartInfo.FileName, but what you need to put in there is the full path to cmd.exe.
Then, in StartInfo.Arguments you need to put "/C dir /b " + my_folder_path + " >> names.txt".
For an explanation of what the /C does, open up a command prompt and type help cmd.
A better way would be:
File.WriteAllLines("names.txt", Directory.GetFiles("c:\\path").Select(Path.GetFileName));
The File, Directory and Path classes are in the System.IO namespace.
I've made a C++ code editor in C# and now am trying to invoke g++ to compile the source files. So I've copied the TDM-GCC installation besides my program and wrote a small batch script to call that.
#echo off
#set PATH="%~dp0TDM-GCC-32\bin\";%PATH%
call "%~dp0TDM-GCC-32\mingwvars.bat"
cd %1
"%~dp0TDM-GCC-32\bin\g++.exe" %2 -o %3
And from C# code, I'm trying to call this script by using CMD like in this script of code.
string fileName = Path.GetFileName(CurrentFile);
string exeName = Path.GetFileNameWithoutExtension(CurrentFile) + ".exe";
string workingDir = Directory.GetParent(CurrentFile) + "";
string compile = Directory.GetParent(Application.ExecutablePath) + "\\compile.cmd";
File.Delete(Path.Combine(workingDir, exeName));
StartProcess(true, "cmd", "/c", "\"" + compile + "\"", workingDir, fileName, exeName);
And here is the StartProcess method:
void StartProcess(bool hidden, string command, params string[] args)
{
ProcessStartInfo pStartInfo = new ProcessStartInfo();
pStartInfo.FileName = command;
pStartInfo.Arguments = string.Join(" ", args);
pStartInfo.UseShellExecute = false;
if (hidden)
{
pStartInfo.RedirectStandardError = true;
pStartInfo.RedirectStandardOutput = true;
pStartInfo.CreateNoWindow = true;
}
Process proc = new Process();
proc.StartInfo = pStartInfo;
proc.Start();
logBox.Clear();
if (hidden)
{
while (!proc.StandardError.EndOfStream)
{
logBox.AppendText(GetTimestamp() + " Error: " + proc.StandardError.ReadLine() + Environment.NewLine);
}
}
}
Update: 26-9-14
I doubted whether the batch file is ever called, because if I call it from cmd, it simply works. So I tried to echo to the standard error from the batch file like this:
echo %PATH% 1>&2
And I can see that the GCC's bin folder is in the path too, but for some reason, the exe is not getting created. But sometimes, the script works, and the exe gets created.
But whenever I execute this, there will be nothing in the LOG and also, no executable is being created. I know that the TDM-GCC's bin folder must be in the PATH, but that is what the second line of the batch script needs to do. Dunno what this is and why this error.
End of update
Any suggestions on how can I get it working?
Thanks.
I don't know if anything written below really helps on this issue. But at least those hints should be useful in any case.
I suggest for the batch file
#echo off
call "%~dp0TDM-GCC-32\mingwvars.bat"
set "PATH=%~dp0TDM-GCC-32\bin;%PATH%"
cd /D %1
"%~dp0TDM-GCC-32\bin\g++.exe" %2 -o %3
PATH is modified after execution of mingwvars.bat. It could be that this batch file also modifies PATH. Or it runs commands like find.exe with the executables expected in %SystemRoot%\System32, but perhaps also existing in directory bin. I have seen already several times not working logon batch scripts because PATH on client computer contained as first folder path the bin directory of a compiler ported from Unix to Windows with executables also found in %SystemRoot%\System32, but working completely different as ported from Unix.
Folder paths should be added to environment variable PATH always without double quotes even if the folder path contains 1 or more spaces. The double quotes used in third line just make sure that a trailing space is not added additionally to PATH and that command set works even with not typical folder paths like an ampersand in path.
And folder paths should be added to environment variable PATH without a trailing backslash.
On command cd the parameter /D is additionally used in case of a change to a different drive must be performed, too. The command cd would not change the current directory if the specified path is on a different drive without parameter /D.
In C# code you have to make sure that workingDir, fileName and exeName are finally on execution of cmd.exe enclosed in double quotes in the arguments string as Erti-Chris Eelmaa already wrote.
And it would be perhaps better to read in your C# application the value of environment variable ComSpec and use this value instead of just cmd for executing the batch file.
This might, or might not be part of the problem, but you don't take care of spaces.
pStartInfo.Arguments = string.Join(" ", args);
you probably would want something like this:
pStartInfo.Arguments = string.Join(" ", args.Select(x => "\"" + x + "\""));
I am writing a C# program that needs to run a script. I want to include the script with the application so that it is available when the user installs the program after I publish it.
I tried adding the script as a resource. In the Solution Explorer, under the Resources directory, I can see the script file.
In the program, I call a function that starts a process and runs the desired command:
runNewProcess("tclsh \\Resources\\make.tcl " + activeProducts);
I get the command prompt with the message "couldn't read file "\Resources\make.tcl": no such file or directory". So I guess it cannot find the file? Am I not referencing the file correctly? Is this the correct way of doing something like this?
Thank you all for your suggestions. Using them and with a bit more research, I was able to come up with a perfect solution for me.
1) Add the TCL script file as a resource to the project and set the Build Action to 'Content' in it's Properties.
2) Get the path to the TCL script (even after installation from a published version):
string makeScriptPath = System.Windows.Forms.Application.StartupPath + "\\Resources\\make.tcl";
3) Construct the run command using all the required variables and pass it to a routine that can execute it.
localCommand = String.Format("tclsh \"{0}\" --librarytype {1} --makeclean {2} --buildcode {3} --copybinary {4} --targetpath \"{5}\" --buildjobs {6} --products {7}",
makeScriptPath, library, makeClean, buildCode, copyBinary, targetPath, buildJobs, activeProducts);
runNewProcess(localCommand);
where:
private void runNewProcess(string command)
{
System.Diagnostics.ProcessStartInfo procStartInfo =
new System.Diagnostics.ProcessStartInfo("cmd", "/k " + command);
procStartInfo.RedirectStandardOutput = false;
procStartInfo.UseShellExecute = true;
procStartInfo.CreateNoWindow = true;
// Now we create a process, assign its ProcessStartInfo and start it
System.Diagnostics.Process proc = new System.Diagnostics.Process();
proc.StartInfo = procStartInfo;
proc.Start();
}
This gives some added perks. Since the file is included with the application, but remains a separate entity, this allows it to be tweaked and modified without needing to re-build, re-publish and re-install the application.
The script runner is unable to dig into you executable to find the commands, as it most likely only know what to do with files on disk. Shipping as a resource is a good idea, but for make anything useful with it you should extract it into a real file on disk so that other programs can use it.
A good pattern for such things would be to create a temporary file on %TEMP%, make the script runner execute that file, and delete it afterwards.
To expand on Alejandro's answer, The easiest way to handle this is to use the temporary folder and copy your script there first.
var scriptPath = Path.Combine(Path.GetTempPath(), "make.tcl");
// Copy the text of the script to the temp folder. There should be a property
//you can reference associated with the script file if you added the file using
//the resources tab in the project settings. This will have the entire script in
//string form.
File.WrteAllText(scriptPath, Resources.make);
runNewProcess("tclsh \"" + scriptPath + "\"" + activeProducts); //added quotes in case there are spaces in the path to temp.
File.Delete(scriptPath); //Clean up after yourself when you are done.
You need to ensure that the Build Action of the script file is set to Content to keep it as an independent file. By default it will be set to Resource which means you will have to programmatically extract it and then save it to a temporary location before attempting to run it.