1

I have little to no experience on C#, but I am very much willing to learn. I am trying to create an application with a button that launches an executable. The application gets ran from an USB Flash drive. Lets say the flash drive has driveletter (e:) on my computer. I want to run a program called rkill.exe from the bin directory.

private void opschonen_RKill_Click(object sender, EventArgs e)
    {
        var process_RKill = new Process
        {
            StartInfo = new ProcessStartInfo
            {
                FileName = "/bin/rkill.exe"
            }
        };
        process_RKill.Start();
        process_RKill.WaitForExit();
    }

However, this does not work. If I launch the application from the root, it does work. I can't point to a driveletter, because not every computer assigns the driveletter to E:

What am I doing wrong? I suppose it's something simple, because I am just a beginner.

Vojtěch Dohnal
  • 7,867
  • 3
  • 43
  • 105

4 Answers4

5
const string relativePath = "bin/rkill.exe";

//Check for idle, removable drives
var drives = DriveInfo.GetDrives()
                      .Where(drive => drive.IsReady
                             && drive.DriveType == DriveType.Removable);

foreach (var drive in drives)
{
    //Get the full filename for the application
    var rootDir = drive.RootDirectory.FullName;
    var fileName = Path.Combine(rootDir, relativePath);

    //If it does not exist, skip this drive
    if (!File.Exists(fileName)) continue;

    //Execute the application and wait for it to exit
    var process = new Process
    {
        StartInfo = new ProcessStartInfo
        {
            FileName = fileName
        }
    };

    process.Start();
    process.WaitForExit();
}
Emile Pels
  • 3,837
  • 1
  • 15
  • 23
0

EDIT Emile Pels' solution seems much more optimized. Assuming it works, I suggest picking that over my quick and dirty fix :)

Given that it works from the root (as you said), the issue is that you're currently using a relative path. It only works when the relative path actually resolves, i.e. when your application is in the root.

To have it work from other locations, you need to use an absolute path. This means you're going to need a drive letter (barring the use of environment variables, which seems WAY out of scope).

I can think of some workarounds to find the correct drive.

Have you application iterate over every possible drive letter. Don't just try to run the .exe for each drive. Before you try to run it, check if the file exists. If it does, you can be pretty sure you're looking at the correct drive.

using System.IO;

public static string LookForDrive(string filepath) //filepath = "\\bin\\myApp.exe")
{
    List<string> possibleLetters = new List<string>() { "A", "B", "C" }; //and so on...

    foreach(string driveLetter in possibleLetters)
    {
        var testPath = String.Format("{0}:\\{1}", driveLetter, filepath);

        if( File.Exists( testPath ) )
        {
            return testPath,
        }
    }

    //If you get here, no drive letter was valid.

    throw new Exception("Could not find the specified file on any possible drive.");
}

The above is just a quick something I threw together. It can probably be optimized further, but I hope the intention is clear.

Flater
  • 12,908
  • 4
  • 39
  • 62
0

You can check what's the address of the removable drive. Then use the address to construct a full path. Note that it's possible to have more than one removable drive so before running process check if your exe is present on the drive:

        var myExe = "/bin/rkill.exe";
        // Find removable drives
        var driveDirectories = 
            DriveInfo.GetDrives()
            .Where(d => d.DriveType == DriveType.Removable)
            .Select(d => d.RootDirectory.FullName);

        foreach (var directory in driveDirectories)
        {
            // create full path
            var fullPath = Path.Combine(directory, myExe);

            // check if path exists
            if (File.Exists(fullPath))
            {
                // execute
                var process_RKill = new Process
                {
                    StartInfo = new ProcessStartInfo
                    {
                        FileName = fullPath;
                    }
                };
                process_RKill.Start();
                process_RKill.WaitForExit();
                // don't try other drives after successful execution
                return;
            }
        }
PiotrWolkowski
  • 8,408
  • 6
  • 48
  • 68
0

As you will run the application from the drive itself, you can just have a method return the drive letter:

private static string GetCurrentDriveLetter()
{
    return Path.GetPathRoot(Environment.CurrentDirectory); // Alternatively, if you believe the working directory may be different
    // return Path.GetPathRoot(Assembly.GetExecutingAssembly().Location);
}

Then apply your current logic, though re-written for using() to make sure it's disposed:

using (Process p = new Process())
            {
                string executable = Path.Combine(GetCurrentDriveLetter(), "/bin/rkill.exe"); // This is preferable to simple string concatenation because it helps prevent some possible issues that may arise otherwise
                p.StartInfo = new ProcessStartInfo(executable);
                //p.StartInfo.UseShellExecute = false; // < Uncomment these two if you see a pop up CLI window
                //p.StartInfo.CreateNoWindow = true;   // <
                p.Start();
                p.WaitForExit();
            }
Dasanko
  • 579
  • 8
  • 16