9

Is it possible to make it so that even after the application file is removed, the application once executed, will still continue running until the end?

I have a portable Console Application that runs on a removable drive and would like it to continue running even if the drive is accidentally removed, is that possible?

I saw http://www.codeproject.com/Articles/13897/Load-an-EXE-File-and-Run-It-from-Memory but it does not seem to work.

pleasega
  • 543
  • 1
  • 3
  • 21
  • 4
    I would do in this way: once app starts it copy file executable and it dependencies in temporary folder, then restart app from temp copy and kill first instance. –  Jun 06 '16 at 07:55
  • The solution from codeproject seems to be another kind of application: starts a second executable (from removable device) from a base executable that keep existing on system drive. –  Jun 06 '16 at 07:57
  • And what happens now if you remove drive? My assumption is this: to be able to run the executable has to be load first. I don't know how exactly loading occurs (you can [investigate](http://reversingproject.info/wp-content/uploads/2009/05/an_in-depth_look_into_the_win32_portable_executable_file_format_part_2.pdf)), but I'd expect what only resources (when are accessed later) is the problem. Pre-load them and use cached version = no need to access disk anymore = no problem. Correct me if I am wrong (tell what happens now). – Sinatr Jun 06 '16 at 08:12
  • https://msdn.microsoft.com/en-us/library/w0628bwh.aspx – Hans Passant Jun 06 '16 at 08:28
  • @Hans Passant: swaprun flag is for C++, so for C# application you need a C++ boostrap? –  Jun 06 '16 at 09:57
  • @ElmoDev001 How do I go about making the copy only once condition? I tried this http://hastebin.com/burawilupu.avrasm but it is looping itself continuously. Is there a way to do this? – pleasega Jun 06 '16 at 10:02
  • Your basic idea is right, you have to debug and check why "filePath" and "tempFilePath" are always different. Another solution is to start application with a proper command line args that you can check at app startup. –  Jun 06 '16 at 10:16
  • I checked by printing the paths and they are the same, but not sure why it just loops. Any ideas? The problem is lying with `Process.Start(tempFilePath)` but can't find out what is the problem. – pleasega Jun 06 '16 at 10:19
  • I think the issue is "Environment.CurrentDirectory" that returns the current directory and not the assembly directory. Check here: http://stackoverflow.com/questions/52797/how-do-i-get-the-path-of-the-assembly-the-code-is-in –  Jun 06 '16 at 10:29
  • But when I print the paths, they are both the same, is that still an issue? – pleasega Jun 06 '16 at 10:32
  • @ElmoDev001 Tried your reference and it worked! Thanks! – pleasega Jun 06 '16 at 10:42

3 Answers3

2

If your console application has some referenced assemblies then they might not be loaded until they are used.

You have to load all related assemblies in your main method or somewhere in the bootstrapper/startup:

var loadedAssemblies = AppDomain.CurrentDomain.GetAssemblies().ToList();
var loadedPaths = loadedAssemblies.Select(a => a.Location).ToArray();

var referencedPaths = Directory.GetFiles(AppDomain.CurrentDomain.BaseDirectory, "*.dll");
var toLoad = referencedPaths.Where(r => !loadedPaths.Contains(r, StringComparer.InvariantCultureIgnoreCase)).ToList();
toLoad.ForEach(path => loadedAssemblies.Add(AppDomain.CurrentDomain.Load(AssemblyName.GetAssemblyName(path))));
Bassam Alugili
  • 16,345
  • 7
  • 52
  • 70
1

The problem with the approach in the article (from your perspective) is that you need to launch it from another application, and that application's binary must be present at all times, so you can't launch that one from the same location, or you haven't got rid of the problem.

One mechanism could be:

  1. User starts app
  2. App copies itself to the temp folder.
  3. App launches the temp copy using Process.Start and a /nocopy parameter
  4. App closes itself.
  5. Temp app starts up, reads the /nocopy parameter and skips the copy and launches.
Richard Matheson
  • 1,125
  • 10
  • 24
  • How will you go about doing the /nocopy parameter? – pleasega Jun 06 '16 at 08:41
  • 1
    It is just a custom command line parameter that you can manage in your application. Just like my comment (" Another solution is to start application with a proper command line args that you can check at app startup"). –  Jun 06 '16 at 12:01
  • In very crude terms, inside `void Main(string[] args)`, just test to see if one of the string in `args` is "/nocopy". Best to check case-insensitively. – Richard Matheson Jun 07 '16 at 06:00
0

The comment by ElmoDev001 led me to achieve the results I wanted.

There are errors but here is the general idea:

Program.cs (Main class)

class Program    {
    static void Main(string[] args)
    {
        String Temp = System.IO.Path.GetTempPath();
        String tmpDir = Temp + @"tmp\";

        String fileName = String.Concat(Process.GetCurrentProcess().ProcessName, ".exe");
        String filePath = Path.Combine(Extension.AssemblyDirectory, fileName);
        String tempFilePath = Path.Combine(tmpDir, fileName);

        if (!Directory.Exists(tmpDir)) { Directory.CreateDirectory(tmpDir); }

        if (!(string.Equals(filePath, tempFilePath))) // if the application is not running at temp folder
        {
            if (!(File.Exists(tempFilePath))) // if the application does not exist at temp folder
            {
                ext.initFile();
            }
            else if (File.Exists(tempFilePath)) // if the application already exists at temp folder
            {
                File.Delete(tempFilePath);
                ext.initFile();
            }
        }
        else if (string.Equals(filePath, tempFilePath)) // if the application is running at temp folder
        {
                //main code
        }
    }
}

Extension.cs

class Extension
{

    String Temp = System.IO.Path.GetTempPath();
    String tmpDir = Temp + @"tmp\";

    String fileName = String.Concat(Process.GetCurrentProcess().ProcessName, ".exe");
    String filePath = Path.Combine(Extension.AssemblyDirectory, fileName);
    String tempFilePath = Path.Combine(tmpDir, fileName);

    public static string AssemblyDirectory
    {
        get
        {
            string codeBase = Assembly.GetExecutingAssembly().CodeBase;
            UriBuilder uri = new UriBuilder(codeBase);
            string path = Uri.UnescapeDataString(uri.Path);
            return Path.GetDirectoryName(path);
        }
    }

    public static void initFile()
    {
            File.Copy(filePath, tempFilePath);
            Process.Start(tempFilePath);
            System.Environment.Exit(0);
    }
 }
Community
  • 1
  • 1
pleasega
  • 543
  • 1
  • 3
  • 21