Is there any way to recognize if the application has been run from a shortcut instead of executable file? I need to make my users to copy exe file to their desktops rather than create shortcuts to it due to personalization issues. Any ideas?
Edit: creating the installer is not an option.
I don't know if this helps, but if you want your exe file to be on the desktop, this could work:
string path = Directory.GetCurrentDirectory();
string desktopPath = Environment.GetFolderPath(Environment.SpecialFolder.Desktop);
if (!path.Equals(desktopPath))
{
Console.WriteLine("file is not at desktop");
}
If you do have an app in the windows shared folder you can configure it to prevent execution of the applications.
Or you can provide user just with link to .bat file instead of .exe and it would do something like this (using robocopy):
robocopy \\remote\server\exe %AppData%\your\folder app.exe /XO
start %AppData%\your\folder\app.exe
And on the C# side you can just check application path and do something like this:
public class Program
{
public int Main()
{
string original_path = System.IO.Path.GetFullPath(#"\\remote\app.exe");
string current_path = System.IO.Path.GetFullPath(
System.Reflection.Assembly.GetExecutingAssembly().Location);
if(original_path == current_path){
System.IO.File.Copy(original_path, #"C:\foo\bar\app.exe", true);
System.Diagnostics.Process.Start(#"C:\foo\bar\app.exe");
return 0;
}
// Run program normally here
}
}
Related
Consider the following statement in a C# console application:
Process.Start("3rdParty/SomeTool.exe");
This statement starts SomeTool.exe in the 3rdParty folder relative to... what, exactly? The folder where the application's .exe resides? The current working directory (which can be changed during the application's lifetime)? Something else?
It is relative to the current working directory of your process.
You can determine your current working directory using Directory.GetCurrentDirectory() and change it using Directory.SetCurrentDirectory().
Well, why don't we find out?
Let's create a simple console application and have some fun with it:
namespace ConsoleApplication1
{
class Program
{
public static void Main()
{
Directory.CreateDirectory("Test");
Console.WriteLine($"Absolute path is: { Path.GetFullPath("Test")}");
Console.ReadLine();
}
}
}
Build it in release mode (we are deploying it after all) and put it in some accesible location. Now double click on it and see what output you get.
Absolute path is: {SomeAccesibleLocationPath}\Test
Hmmm, it seems like the relative path is relative to the directory where the executable was launched. Is this always so?
Let's build another app, we'll call it ConsoleApplication2, and play some more:
class Program
{
public static void Main()
{
Console.WriteLine($"TEST #1: {LaunchProcessAndGetAbsolutePath()}");
Console.WriteLine($"TEST #2: {LaunchProcessAndGetAbsolutePath(Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments))}");
Console.ReadLine();
}
private static string LaunchProcessAndGetAbsolutePath(string workingDirectory = null)
{
var startInfo = new ProcessStartInfo(#"{SomeAccesibleLocationPath}\ConsoleApplication1.exe");
startInfo.RedirectStandardOutput = true;
startInfo.RedirectStandardInput = true;
startInfo.CreateNoWindow = true;
startInfo.UseShellExecute = false;
if (workingDirectory != null)
{
startInfo.WorkingDirectory = workingDirectory;
}
using (var p = Process.Start(startInfo))
{
var ret = p.StandardOutput.ReadLine();
p.StandardInput.WriteLine();
p.WaitForExit();
return ret;
}
}
}
If we run this, you'll see that the output is the following:
TEST #1: {MyConsoleApplication2ExecutableDirectory}\Test
TEST #2: {MyDocumentsPath}\Test
Important facts to consider:
The relative paths are always relative to the working directory.
When starting a process from another process, the default working directory is the working directory of the "parent" process, its not the directory of the launched "child" process.
When launching a process by double clicking on the executable, the working directory is set to the executable's directory.
In general, the working directory need not be the executable's directory. Your program's correctness should not rely on this condition ever.
As mentioned, it is relative to the current working directory. The value can be changed and determined via Directory.SetCurrentDirectory(), resp. Directory.GetCurrentDirectory()
Some notes:
The current working directory CAN be the folder where the executable resides; this is the default working directory if none is specified. But it can also be set to a different folder when a process is started or while running. On start either by setting the property ProcessStartInfo.WorkingDirectory when launching a process, or also by using the working directory field in the symbolic link dialog in e.g. the start menu. The latter can be done by a user. The current working directory CAN also be changed by any 3rd party library that is being loaded into the process.
Therefore relative paths in an application without checking the working directory are unreliable and usually cause unexpected behavior.
So I'm learning how to develop software and I'm running into a problem. When I create a form in Visual Studio and have it open a document or open something else when I click a button I have it pointing here:
C:\User\MyName\Documents\TestApp\test.txt
What I want to know is how do I get it to where the program just looks at TestApp folder vs going through the C: Drive? Say all the files are needed for the program to run are located in the TestApp folder.
If you know that your app is going to be run from the same place every time (like a folder) you can call the GetCurrentDirectory() method. This will return a string of the current directory that your app is running from.
String pwd = GetCurrentDirectory(); //Contains something like C:\Users\Daedric\TestApp\
String finalString = Path.Combine(pwd, "test.txt"); //As per Corak
You need start file from your app folder?
Application.StartupPath
for start file
Process.Start(Application.StartupPath + #"\test.txt");
Besides above answer, following example can help you understand how Path works:
class Program
{
static void Main()
{
string[] pages = new string[]
{
"cat.aspx",
"really-long-page.aspx",
"test.aspx",
"invalid-page",
"something-else.aspx",
"Content/Rat.aspx",
"http://dotnetperls.com/Cat/Mouse.aspx",
"C:\\Windows\\File.txt",
"C:\\Word-2007.docx"
};
foreach (string page in pages)
{
string name = Path.GetFileName(page);
string nameKey = Path.GetFileNameWithoutExtension(page);
string directory = Path.GetDirectoryName(page);
//
// Display the Path strings we extracted.
//
Console.WriteLine("{0}, {1}, {2}, {3}",
page, name, nameKey, directory);
}
}
}
Sample output would be like this:
Input C:\Windows\File.txt
GetFileName: File.txt
GetDirectoryName: C:\Windows
GetCurrentDirectory is not the correct method to use as the return value can change while you application is running. You can see this by simply running the following in a console app:
Console.WriteLine(Directory.GetCurrentDirectory());
Directory.SetCurrentDirectory(#"c:\temp\");
Console.WriteLine(Directory.GetCurrentDirectory());
You can use the following to give you the assembly location:
Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location)
I have a requirement where I need to run a particular .exe based on the architecture.
My folder structure is like this:
The Tools folder looks like this
Under that the Binaries folder contains 2 sub folder (one each for 32/64 bit)
Each of these folders (x64/x32) looks like below:
The Root folder contains the .bat (start.bat) file that calls the appropriate exe (For example: if it is 32bit, it calls "\Binaries\x86\Tool.exe" or if it is 64bit, it calls "\Binaries\x64\Tool.exe".
The code in start.bat is as below:
#ECHO OFF
if exist "%SYSTEMDRIVE%\Program Files (x86)\" (
start "" /d "%~dp0" "Binaries\x86\Tool.exe"
) else (
start "" /d "%~dp0" "Binaries\x64\Tool.exe"
)
The calling is fine and it calls the particular .exe. The problem comes when the .exe application tries to use the XML file (each folder also contains a XML file, parameters.xml, along with exe) it throws an error. I am accessing the XML file using relative paths like ("./parameters.xml").
I tried recoding the code by using "System.AppDomain.CurrentDomain.BaseDirectory" (as this is a WPF exe). That works for relative path, but another scenario fails. I will explain the same below:
In the application i am getting the instances of SQL installed on the machine. To achieve that I am using the following code:
internal static List < string > SQLServerInstances() {
var sqlInstances = new List < string > ();
try {
using(RegistryKey sqlKey = RegistryKey.OpenRemoteBaseKey(RegistryHive.LocalMachine, "").
OpenSubKey(#
"SOFTWARE\Microsoft\Microsoft SQL Server\Instance Names\SQL")) {
if (sqlKey != null) {
foreach(string versionKeyName in sqlKey.GetValueNames()) {
sqlInstances.Add(versionKeyName);
}
}
}
} catch (Exception de) {
throw de;
}
}
This code returns no instances if I use the batch file to run the .exe. If I run the .exe directly from "Binaries\x64\Tool.exe", this code passes and returns me the SQL instances properly.
I am not sure what the issue is. This might be expected behaviour but seems a bit weird.
Try changing your start.bat code this way:
#ECHO OFF
cd "%~dp0"
if exist "%SYSTEMDRIVE%\Program Files (x86)\" (
start "" "Binaries\x86\Tool.exe"
) else (
start "" "Binaries\x64\Tool.exe"
)
Why wont the files in the test folder delete?? How can i get admin access??
namespace Delete
{
using System;
using System.Windows.Forms;
using System.IO;
public class Delete
{
public Delete()
{
if (Directory.Exists(#"C:\Program Files (x86)\test\"))
{
string[] filePaths = Directory.GetFiles(#"C:\Program Files (x86)\test\");
foreach (string file in filePaths) { File.Delete(file); }
}
}
}
}
You need to rethink your strategy.
If you are adding/removing files programatically from within your application, they should be stored in a separate location (that won't need admin privs to elevate for writing/deleting, etc.):
like the user's data directory/your company/your application, or
the user's documents/your company/your application
The Program Files directory is for application specific files (DLL's, etc) that are installed with the program but don't change once installed/updated.
Here's an example of the User's Data directory by application:
public static DirectoryInfo ApplicationVersionDirectory()
{
return new DirectoryInfo(System.Windows.Forms.Application.UserAppDataPath);
}
This is due to the UAC. So either run your executable as admin by right clicking -> "Run as Administrator" or if you want to do it programatically refer to other posts like Windows 7 and Vista UAC - Programmatically requesting elevation in C#
In order to delete files from "Program Files" folder you need to start application as an administrator. Otherwise you will not be able to get an access to %PROGRAMFILES%.
Here is the sample code to restart current app and run it as admin:
ProcessStartInfo proc = new ProcessStartInfo();
proc.UseShellExecute = true;
proc.FileName = Application.ExecutablePath;
proc.Verb = "runas";
try
{
Process.Start(proc);
}
catch
{
// The user refused the elevation.
// Do nothing and return directly ...
return;
}
Application.Exit(); // Quit itself
In my application, I have defined the following:
public static readonly string NOTEPAD = "%windir%\\notepad.exe";
I can type in the text value of NOTEPAD into the Run command on my Win7 machine, and Notepad will open.
However, from within my Visual Studio C# project, the Write Line routine will fire every time:
if (!File.Exists(NOTEPAD)) {
Console.WriteLine("File Not Found: " + NOTEPAD);
}
Does Visual Studio not understand %windir%?
Instead of expanding the variable manually as suggested by the other answers so far, you can have the Environment class do this for you just like the Run command does:
if (!File.Exists(Environment.ExpandEnvironmentVariables(NOTEPAD))) {
Console.WriteLine("File Not Found: " + NOTEPAD);
}
See http://msdn.microsoft.com/en-us/library/system.environment.expandenvironmentvariables.aspx
When looking on my windows XP box, the location of notepad is:
%SystemRoot%\system32\notepad.exe
Not:
%windir%\notepad.exe
You also need to make sure that these environment variables are resolved correctly - use Environment.GetEnvironmentVariable and Path.Combine to build up the correct path:
string root = Environment.GetEnvironmentVariable("SystemRoot");
string path = Path.Combine(root, "system32", "notepad.exe");
Just have a closer Look at the Class Environment. The Environment Variable is SystemRoot, so you can use
Environment.GetEnvironmentVariable("windir") (or something like that)
http://msdn.microsoft.com/en-us/library/system.environment.getenvironmentvariable.aspx
The console "Resolves" the %windir% environment variable to the correct path. You need to use the above function to do the same within your application.
Use Environment.GetEnvironmentVariable("windir");
So you could declare it like this:
public static readonly string NOTEPAD = Environment.GetEnvironmentVariable("windir") + "\\notepad.exe";