Embed all files into a single exe - c#

I would like to embed all files/resources into a single exe (2 exe's and a ps1)
Is it possible ? and how would one go about doing it, currently I have managed to publish the project but there are additional files ( Application Files, Application Manifest and setup exe) in the output - this I would like to embed into single exe.
Been doing some readings, seems I may need to embed the resources and perhaps create pointers? or embedding image resources in the project, not sure if I am on right track and/or how to start the code..
What I have so far
private Boolean Install_certificate()
{
try
{
var newProcessInfo = new System.Diagnostics.ProcessStartInfo()
{
FileName = #"C:\Windows\SysWOW64\WindowsPowerShell\v1.0\powershell.exe",
Verb = "runas",
Arguments = (#"–ExecutionPolicy Bypass ""script.ps1""")
};
System.Diagnostics.Process.Start(newProcessInfo);
// Install the cert & change proxy settings
//System.Diagnostics.Process.Start(#"script.ps1" );
return true;
}
catch
{
return false;
}
}
private Boolean Install_exe1()
{
try
{
// Install exe1
System.Diagnostics.Process.Start(#"setup1.exe");
return true;
}
catch
{
return false;
}
}
private Boolean Install_exe2()
{
try
{
// Install exe2
System.Diagnostics.Process.Start(#"setup2.exe");
return true;
}
catch
{
return false;
}
}
Appreciate the help!
Thanks

You can use System.Diagnostics.Process to start .exe files.
You can use System.Management.Automation to run power shell scripts.
If your exes work with their dependencies from a command prompt you shouldn't have a problem in C#
If you want to embed these files into your c# project you can add them as resources and when your program runs you can save their binary content to an exe file and then execute them:
In a nutshell, right click on your project, choose properties and then click on resources. Click the link to add a default resource file to your project,
Then in the resource designer select the files, then add resource and add the files you wish to embed. This will create a Resources file that will have your files embedded in them that you can access as a byte[] from in your application.
Then when you application starts save them to disk and run them:
using System;
using System.IO;
using System.Diagnostics;
namespace myNs
{
class Program
{
static void unpack()
{
if (!File.Exists("exe1.exe"))
File.WriteAllBytes("exe1.exe", myNs.Properties.Resources.exe1);
if (!File.Exists("exe2.exe"))
File.WriteAllBytes("exe2.exe", myNs.Properties.Resources.exe2);
if (!File.Exists("ps1.ps")) if (!File.Exists("ps1.ps"))
File.WriteAllBytes("ps1.ps", myNs.Properties.Resources.ps1);
}
static void Main(string[] args)
{
unpack();
Process process = new Process();
process.StartInfo.FileName = "exe1.exe";
process.Start();
process.WaitForExit();
process = new Process();
process.StartInfo.FileName = "exe2.exe";
process.Start();
process.WaitForExit();
process = new Process();
process.StartInfo.FileName = #"C:\Windows\SysWOW64\WindowsPowerShell\v1.0\powershell.exe";
process.StartInfo.Arguments = "ps1.ps1";
process.Start();
process.WaitForExit();
}
}
}

my question is regarding compiling the exe's + powershell into a single exe without the additional files
You can add the ps1 script as a resource like Alexander Higgins illustrates.
You can merge 2 assemblies together using the tool ILMerge from Microsoft. Here we merge Primary.dll Secondary.dll (and etc) into Merged.dll with a log.txt out of the merger.
ilmerge /log:log.txt /out:Merged.dll Primary.dll Secondary.dll
Because EXEs have an entry point you cant megre 2 EXEs, to workaround this simply change one of the EXEs to be a DLL (class library).

Related

Execute multiple command lines with the different process using .NET

i want to use CMD.exe in my C# Program.
The Problem is iam using normally The CMD to open Two Programs to convert Fotos from .png to .svg (ImageMagick.exe & Potrace.exe).
That happend via CMD.exe with Tow Command-lines
the firsstepe: magick convert image.png image.pnm the secondstep: potrace image.pnm image.svg
How to call the CMD.exe to do this two commandslin in my C# Program ?
i try this commands Lines but he call just the CMD.exe and do not anything.
If you use the C# Process class as shown in the code below, with process.WaitForExit() called after each process is started, then you can run the two commands separately as shown. I think the correct calls are in fact magick convert image.png image.pnm and then potrace image.pnm -b svg. This works for me and is what the code is doing.
The biggest problem here was getting C# to find the files it needs. To get this to work I put potrace in a subfolder of the project and copied it to the output directory, so you don't need to install it. I couldn't get this to work for ImageMagick. It appears to need to be installed to work, so in the end I just installed it. The files to be converted are similarly in a Files subfolder of the project with 'Copy to Output Directory' set on their project properties.
As some of the commenters have suggested it may be better to try one of the libraries that can be called directly from C# rather than starting separate processes, which are clearly more inefficient. However, my experience is that often this sort of wrapper project is not well-maintained versus the main project, so if the solution below works it may be fine.
I can upload the full project to GitHub if that would be helpful.
using System.Diagnostics;
using System.Reflection;
#nullable disable
namespace ImageMagicPotrace
{
internal class Program
{
static void Main(string[] args)
{
string path = Path.GetDirectoryName(Assembly.GetAssembly(typeof(Program)).Location);
string filesPath = Path.Combine(path, "Files");
string fileName = "image";
string imageMagick = "magick.exe";
string arg = $"convert \"{Path.Combine(filesPath, fileName + ".png")}\" \"{Path.Combine(filesPath, fileName + ".pnm")}\"";
RunProcess(imageMagick, arg);
string potrace = Path.Combine(path, #"potrace\potrace.exe");
string arg2 = $"\"{Path.Combine(filesPath, fileName + ".pnm")}\" -b svg";
RunProcess(potrace, arg2);
Console.WriteLine("Done");
}
private static void RunProcess(string executable, string arg)
{
Console.WriteLine(executable);
Console.WriteLine(arg);
ProcessStartInfo start = new ProcessStartInfo(executable)
{
WindowStyle = ProcessWindowStyle.Hidden,
Arguments = arg,
UseShellExecute = false,
CreateNoWindow = true
};
Process process = Process.Start(start);
process.WaitForExit();
}
}
}

Using a .R file as a resource and create a batch directly in C#

I have a number of shiny applications with the file structure global.R, ui.R, server.R and something I call batchTrigger.R. The contents of the latter is simply the following-
.libPath(*Path to my R Package Repository*)
require('shiny')
runApp(*Path to the folder with the aforementioned files*)
I created a batch file called application.cmd with the following code-
cls
#pushd ""
:::::::::::::::::::
#echo off
ECHO Loading...Please, wait. The Application will open automatically.
ECHO ---
ECHO Do not close this console window for the whole duration of your session
ECHO in the application.
ECHO ---
#echo off
"C:\Program Files\R\bin\Rscript.exe" ".../**batchTrigger.R**"
:::::::::::::::::::
#popd
cmd /k
This batch file is working just fine. Then I went one step further, and decided to create a windows form with multiple R Applications. I have two buttons in the form, each of which goes something like this-
private void application1_click(object sender, EventArgs e)
{
System.Diagnostics.Process cmd = new Process();
cmd.StartInfo.UseShellExecute = false;
cmd.StartInfo.FileName = "...\\**application1.cmd**";
cmd.StartInfo.Arguments = "/K";
cmd.StartInfo.CreateNoWindow = false;
cmd.StartInfo.RedirectStandardInput = true;
cmd.Start();
}
So far, so good. Both the buttons work exactly as they were supposed to. I want to go one more step ahead, but since I am very new at C#, I need help. What I am hoping to get is a dynamic location for the R files and the cmd files within the thus deployed application, within the solution. In other words, I should be able to write the contents of the batch file within the C# code, and the path of the batchTrigger.R should be something which changes with the location of the windows form application (which will be a self contained deployed executable file). The idea is that the R package repository and R installation may remain static and can be pointed at by the batchTrigger.R and application.cmd respectively, but the location of batchTrigger.R itself along with other R files move with the application. I think that resource.resx can do something about this, but how exactly can I go about doing it, I don't seem to get. Any suggestion would be highly appreciated.
Make a general method:
private void StartSilentR(string rScriptFilePath)
{
System.Diagnostics.Process cmd = new Process();
cmd.StartInfo.UseShellExecute = false;
cmd.StartInfo.FileName = #"C:\Program Files\R\bin\Rscript.exe";
cmd.StartInfo.Arguments = rScriptFilePath;
cmd.StartInfo.CreateNoWindow = false;
//avoid this unless you must control the app via stdin
//cmd.StartInfo.RedirectStandardInput = true;
cmd.Start();
}
Then write something that calls it after working out where the script is. For example if you have your directory structure as:
RLauncherCSharpApp.exe
rscripts\ui.R
rscripts\global.R
rscripts\batchTrigger.R
Then in c# you can:
//take exe Path, remove exe name and add rscripts folder and batchtrigger.R file name
var rbt = Path.Combine(Path.GetDirectoryName(Application.ExecutablePath), "rscripts", "batchTrigger.R");
StartSilentR(rbt);
Or, say you want to search all the subfolders of the app's folder looking for all files called batchTrigger.R:
var exeFolder = Path.GetDirectoryName(Application.ExecutablePath);
string[] paths = Directory.GetFiles(exeFolder, "batchTrigger.R", SearchOption.AllDirectories);
//maybe add them to a list view and the user can click one to launch ..

How to zip a Folder using SSIS 2008 without using any third party s/w?

I have a scenario where i would like to compress my folder due to presence of large number of files present in them using SSIS 2008. Consider it like i have one Source Folder and one Target Folder and while moving files from "SRC" to "TGT" the folder must be compressed in destination.Now feasible option for doing this i think is SSIS Script task ,since I cannot use Execute Process task due to restriction of using any third party software like 7z/Winrar etc.But i am not able to implement this even after using SSIS Script Component.Tried many online solutions but it did not work.How can i implement such thing using SSIS 2008?
You can use the ZipPackage class if you are targeting .Net 3 and above. Complete example here:
https://msdn.microsoft.com/en-us/library/system.io.packaging.zippackage.aspx
There is also a ZipArchive class, example here:
https://msdn.microsoft.com/en-us/library/system.io.compression.ziparchive(v=vs.110).aspx
I did this exercise , I created a Script Task to perform compression of a folder by using the compression provided by Windows;
the folder name can dynamically change.
In this way it is not necessary to use third party software like 7z/Winrar etc..
You need to provide to the Script Task the folder to be zipped and the name of the compressed folder as ReadOnlyVariables (to be added in the tab ReadOnlyVariables)
These two variables must be defined in the Variables tab (String type) of the package and can be changed dynamically through a cycle (eg. for each)
I use these two variables:
sFolderCompressed - the folder '.zip' that you want to obtain eg. \\XX.XX.XX.XX\C$\.....\folderCompressed
sFolderSource - the source folder containing the files affected eg. \\XX.XX.XX.XX\C$\.....\folderSource
(*)
The script is made using c#, choose Script Language: Microsoft Visual C# 2008
This is the code to be added in the Main method:
public void Main()
{
// TODO: Add your code here
try
{
// variables used in process
string l_sFolderCompressed = (string)Dts.Variables["User::sFolderCompressed"].Value;
string l_sFolderSource = (string)Dts.Variables["User::sFolderSource"].Value;
string l_sCommand = "zip -j " + l_sFolderCompressed + " " + l_sFolderSource + "/*";
// create the ProcessStartInfo using "cmd" as the program to be run,
// and "/C " as the parameters.
// Incidentally, /C tells cmd that we want it to execute the command that follows,
// and then exit.
System.Diagnostics.ProcessStartInfo procStartInfo = new System.Diagnostics.ProcessStartInfo("cmd", "/C " + l_sCommand);
// The following commands are needed to redirect the standard output.
// This means that it will be redirected to the Process.StandardOutput StreamReader.
procStartInfo.RedirectStandardOutput = true;
procStartInfo.UseShellExecute = false;
// Do not create the black window.
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();
// Get the output into a string
string result = proc.StandardOutput.ReadToEnd();
// Possibly display the command output.
}
catch (Exception objException)
{
Dts.TaskResult = (int)ScriptResults.Failure;
// Log the exception
}
Dts.TaskResult = (int)ScriptResults.Success;
}
You can also manage a single file
"cmd", "/C zip -j c:\\...\file.zip c:\\..\file.txt");
I hope can help

Can't Delete folder as it's in use.

I have an issue with an auto update software that I am trying to make. The application runs as a server application and checks via FTP for updates and downloads them if there is a newer version available. This then unzips a folder called update in the programs root directory. it then launches a file called update.bat that does any file copying etc that I may need to do for that update. Once this is finished the update.bat launches the new server application. Once the program goes to check for updates again it is suppose to delete the update directory that is in the root directory of the server application as well as the update.rar file that was downloaded from the update server. All of this works perfectly except the folder is being used and will not delete. I have read all kinds of things about releasing the handle and changing the current directory etc.. but just can't seem to get it to work. I would appreciate someone helping me out here. Here is the code for this update.
private void Form1_Load(object sender, EventArgs e)
{
foreach (string s in Directory.GetDirectories("C:/my update dir"))
{
if (s.Contains("Instance"))
{
var _instance = Regex.Match(s, #"\d+");
Process p = new Process();
ProcessStartInfo pinfo = new ProcessStartInfo();
pinfo.FileName = "cmd.exe";
pinfo.WorkingDirectory = "C:/mySQL/bin";
pinfo.Arguments = "/C mysql.exe -u** -p** dbnameHere" + _instance.ToString() +
" < \"C:/my update dir/update/update.sql\"";
p.StartInfo = pinfo;
p.Start();
p.WaitForExit();
p.Close();
p.Dispose();
Directory.SetCurrentDirectory("C:/");
}
}
Directory.SetCurrentDirectory("C:/");
this.Dispose();
Application.Exit();
}
I'm going to guess that your problem is here:
Once this is finished the update.bat launches the new server
application.
Windows is going to "lock" the directories all the way down to the .BAT file. So if the .BAT runs a server process from that directory, that process is going to inherit the CWD and file descriptors of the calling process.
It's not clear from your code that this is what's happening, but you may also want to try changing the CWD prior to spawning the process.

Programmatically setting startin location when starting a process

I have an application that creates a shortcut on my desktop and allows you to drag and drop files into the shortcut to perform an action (convert a word document to PDF). Now what I am trying to do is perform this action programmatically using shellexecute (.NET Process.Start()).
The problem is that it doesnt seem to be working and I have a sneaking suspicion this has something to do with the fact that the shortcut created has the "Start in" parameter set to a specific folder.
So it looks like this:
Shortcut target: "C:\Program Files (x86)\MyPDFConvertor\MyPDFConvertor.exe"
Shortcut startin: "C:\Program Files (x86)\MyPDFConvertor\SomeSubfolder\SomeSubSubFolder"
My code was the following.
System.Diagnostics.Process.Start("C:\\Program Files (x86)\\MyPDFConvertor\\MyPDFConvertor.exe", "C:\\MyFiles\\This is a test word document.docx");
Fundamentally my question boils down to: What does "Startin" actually mean/do for shortcuts and can I replicate this functionality when starting an application using either shellexecute or Process.Start?
When you use Process.Start you can call it with a ProcessStartInfo which in turn happens to be able to setup a WorkingDirectory property - this way you can replicate that behaviour.
As Yahia said, set the WorkingDirectory property. You also need to quote the arguments. Here is a rough example:
//System.Diagnostics.Process.Start("C:\\Program Files (x86)\\MyPDFConvertor\\MyPDFConvertor.exe", "C:\\MyFiles\\This is a test word document.docx");
ProcessStartInfo start = new ProcessStartInfo();
//must exist, and be fully qualified:
start.FileName = Path.GetFullPath("C:\\Program Files (x86)\\MyPDFConvertor\\MyPDFConvertor.exe");
//set working directory:
start.WorkingDirectory = Path.GetFullPath("C:\Program Files (x86)\MyPDFConvertor\SomeSubfolder\SomeSubSubFolder");
//arguments must be quoted:
const char quote = '"';
start.Arguments = quote + "C:\\MyFiles\\This is a test word document.docx" + quote;
//disable the error dialog
start.ErrorDialog = false;
try
{
Process process = Process.Start(start);
if(process == null)
{//started but we don't have access
}
else
{
process.WaitForExit();
int exitCode = process.ExitCode;
}
}
catch
{
Console.WriteLine("failed to start the program.");
}

Categories