I'm trying to create a short method that opens an image in the default image viewer, and after time milliseconds closes it.
Right now it looks like this:
public static async void OpenImage(string path, int time)
{
var process = new Process();
process.StartInfo.FileName = path;
process.Start();
await Task.Delay(time);
process.Kill()
process.Close()
}
I can see the image, but then process.Kill() throws an InvalidOperationException, "No process is associated with this object."
Have I missed something or is there any other way to do it?
Update:
Also tested this now:
public static async void OpenImage(string path, int time)
{
var process = Process.Start(path)
await Task.Delay(time);
process.Kill()
process.Close()
}
But then Process.Start() returns null. So maybe I have to call on the .exe directly like faljbour commented?
The problem here is that you are not really starting a process, but rather passing a file path to the Windows Shell (explorer.exe) to handle. The shell figures out how to open the file and it starts the process.
When this happens your code doesn't get the process id back, so it doesn't know which process to kill.
What you should do, is find the default application for the file and then start that application explicitly (rather than letting the shell figure it out).
The most compact way I can think of to find the default application for a file is to use the Win32 API FindExecutable().
Things are complicated a little when the default application is contained within a dll. This is the case with the default Windows photo viewer (C:\Program Files (x86)\Windows Photo Viewer\PhotoViewer.dll). Since it is not a exe you cannot start it directly, however the application can be started using rundll32.
This should work for you:
[DllImport("shell32.dll")]
static extern int FindExecutable(string lpFile, string lpDirectory, [Out] StringBuilder lpResult);
public static async void OpenImage(string imagePath, int time)
{
var exePathReturnValue = new StringBuilder();
FindExecutable(Path.GetFileName(imagePath), Path.GetDirectoryName(imagePath), exePathReturnValue);
var exePath = exePathReturnValue.ToString();
var arguments = "\"" + imagePath + "\"";
// Handle cases where the default application is photoviewer.dll.
if (Path.GetFileName(exePath).Equals("photoviewer.dll", StringComparison.InvariantCultureIgnoreCase))
{
arguments = "\"" + exePath + "\", ImageView_Fullscreen " + imagePath;
exePath = "rundll32";
}
var process = new Process();
process.StartInfo.FileName = exePath;
process.StartInfo.Arguments = arguments;
process.Start();
await Task.Delay(time);
process.Kill();
process.Close();
}
This code demonstrates the concept, but if you want to cater for more default applications with unusual argument formats (as shown by photoviewer.dll), you should search the registry yourself or use a third party library to find the correct command line to use.
For example,
http://www.codeproject.com/Articles/17023/System-File-Association
Related
1-) I create a Python .exe which includes this code:
def main():
args = parse_arguments()
result = []
paths = args.files
regions = args.regions
oddplate = args.oddplate
result = []
#print ("Input args ")
#print (args)
#print ("\n")
if not args.sdk_url and not args.api_key:
raise Exception('api-key is required')
if len(paths) == 0:
print('File {} does not exist.'.format(args.FILE))
return
elif args.blur_dir and not os.path.exists(args.blur_dir):
print('{} does not exist'.format(args.blur_dir))
return
....
print(result)
return result
2-) Then, i create a c# function from .net (lets call it ProcessFunc), which includes this code
ProcessStartInfo myProcessStartInfo = new ProcessStartInfo();
// make sure we can read the output from stdout and that window is not shown
myProcessStartInfo.UseShellExecute = false;
myProcessStartInfo.RedirectStandardOutput = true;
myProcessStartInfo.CreateNoWindow = true;
myProcessStartInfo.FileName = dir_app;
// start python app with 9 arguments
myProcessStartInfo.Arguments = " " + Iapi_key + " " + Isdk_url + " " + Iregions + " " + Iblur_amount + " " + Ioddplate + " " + Iblur_dir;
Process myProcess = new Process();
// assign start information to the process
myProcess.StartInfo = myProcessStartInfo;
// start the process
myProcess.Start();
// Read the standard output of the app we called.
// in order to avoid deadlock we will read output first
// and then wait for process terminate:
StreamReader myStreamReader = myProcess.StandardOutput;
string myString = myStreamReader.ReadLine();
/*if you need to read multiple lines, you might use:
string myString = myStreamReader.ReadToEnd() */
// wait exit signal from the app we called and then close it.
myProcess.WaitForExit();
myProcess.Close();
// write the output we got from python app
//Console.WriteLine("Value received from script: " + myString);
return myString;
3-) I have 3 drives, C,N(local machine) and Z (net shared drive)
Having c# code compiled (which generates a .exe) i called the python generated .exe 2 ways:
Double clicking the c# .exe after introducing that code in a main program (lets call it MyCProgramMain)
static void Main()
{
ProcessFunc();
}
This works correctly (Iblur_dir parameter path is accepted)
Generating a service:
static void Main()
{
System.Threading.Thread.CurrentThread.CurrentCulture = new
System.Globalization.CultureInfo("es-ES");
ServiceBase[] ServicesToRun;
ServicesToRun = new ServiceBase[]
{
new MyService()
};
ServiceBase.Run(ServicesToRun);
}
Which will create a Thead:
public MyService()
{
InitializeComponent();
HEjecucion = new Thread(classEjecucion.ProcessFunc);
HEjecucion.Start();
Afterwards i will start the generated Service.
When doing it this way, i will get this message in my log (for example):
Z:\2021-08-18\14 does not exist
This will only happen with paths in Z:\ drive, N:\ and C:\ will be accepted.
So, when calling the Python.exe through a Main program focused generated c# .exe
*os.path.exists(args.blur_dir)* understands the path exists
but if i call that same Python.exe through a Service generated with C#, it is unable to access args.blur_dir path
I have tried:
Creating a app.manifest in c#project which includes
(perhaps it was related to priviliges)
Playing around with myProcessStartInfo parameters to see if something could make
I have spent lot of time trying multiple things related to C# Process() parameters, but nothing seems to work in order to make the Service 'reach' the dir_blur path. ¿What else could i try?
You are alright guys! Problem was i was working on a remote desktop in where i was login with an specific user.
Service was automatically installed as 'Local system', and all i had to do was go to Services, in my service "properties" and in 'login' tab introduce credentials i use for remote desktop access.
Im making an application which needs to monitor the filesystem using FileSystemWatcher, to detect how an installation affects the filesystem.
To get rid of noise i want to filter the events that are created by their creating user, and that code is working with the //BUILTIN //Administrator user, which is used by default when doing an installation. But still there are quite a bit of noise. Then i got the idea of creating a specific user that i can use for running the installation file, and filter on that specific user, and thereby getting rid of allmost all the noise.
this is my code for the process creation and start
private void executeInnoInstaller(string path, string fileName)
{
// Use ProcessStartInfo class
ProcessStartInfo installerProces = new ProcessStartInfo();
installerProces.CreateNoWindow = true;
installerProces.UseShellExecute = false;
installerProces.FileName = path + "\"" + fileName + "\"";
installerProces.WindowStyle = ProcessWindowStyle.Normal;
installerProces.UserName = "test";
System.Security.SecureString encPassword = new System.Security.SecureString();
foreach (System.Char c in "test")
{
encPassword.AppendChar(c);
}
encPassword.MakeReadOnly();
installerProces.Password = encPassword;
try
{
// Start the process with the info we specified.
// Call WaitForExit and then the using statement will close.
using (Process exeProcess = Process.Start(installerProces))
{
exeProcess.WaitForExit();
//int exitCode = exeProcess.ExitCode;
}
}
catch (Exception e)
{
Console.WriteLine(e);
}
}
this code exits with a access denied.
OS=Windows
Ive already tried to run the installer.exe from the OS filehandler with SHIFT - Rightclick using the specified user, and it works.
VisualStudio is run as administrator.
Ive tried to run the build project exe file as administrator, but it does not work.
Without the user credentials, the code works and uses the //BUILTIN //Administrator account
Does anybody have any idea ?
Thank you beforehand for your time and effort.
This code works if i turn down the UAC securitylevel to the lowest.
I am trying to open different files with their associated applications like this:
ProcessPath = #"C:\Users\Public\Pictures\Sample Pictures\Tulips.jpg";
ProcessStartInfo processStartInfo = new ProcessStartInfo();
processStartInfo.FileName = ProcessPath;
processStartInfo.WorkingDirectory = System.IO.Path.GetDirectoryName(ProcessPath);
process = new Process();
process.StartInfo = processStartInfo;
process.EnableRaisingEvents = true;
process.Exited += Process_Exited;
process.Start();
Please can you help me get hold of the Process object that represents the application opened because after the call to process.Start(); most of the fields inside process have threw exceptions.
Thanks.
I have managed to find a solution:
using the Windows API function
[DllImport("shell32.dll")]
static extern int FindExecutable(string file, string directory, [Out] StringBuilder lpResult);
to return the associated application path for a given file. I can then use the original code i posted to execute that associated application with the original file passed through as an argument!
I've been trying to make a program to automate the process of running different processes on my computer. So far I've got the following program running a console version of BleachBit(It's like CCleaner), the process appears in task manager, it hits around 25kb process RAM then CPU usage goes to 0% and just sits there doing nothing for ages and never quits.
Is there something wrong I'm doing in my code that could cause this to happen?
I've tried editing the app.manifest to make sure the program has to be run as admin in case it needed more privileges
Also when running similar code in a bat file to run the program, it's opens its own windows and runs fine, so I'm not sure. Any help in the right direction would be fantastic.
The code I'm running is below.
static void Main(string[] args)
{
string Log = "";
if (File.Exists(Environment.CurrentDirectory + "\\BleachBit\\bleachbit_console.exe"))
{
Log += "File exists";
Log += RunProgramCapturingOutput("\\BleachBit\\bleachbit_console.exe", "--preset --clean");
}
else
Log += "Program not found. Please place at \\BleachBit\\bleachbit_console.exe";
File.WriteAllText("log.txt", Log);
Console.ReadLine();
}
public static string RunProgramCapturingOutput(string filename, string arguments)
{
ProcessStartInfo processInfo = new ProcessStartInfo()
{
FileName = Environment.CurrentDirectory + filename,
Arguments = arguments,
CreateNoWindow = false,
UseShellExecute = false,
WorkingDirectory = Path.GetDirectoryName(Environment.CurrentDirectory + filename),
RedirectStandardError = false,
RedirectStandardOutput = true
};
Process process = Process.Start(processInfo);
process.WaitForExit();
string output = output = process.StandardOutput.ReadToEnd();
Console.WriteLine("Output: " + output);
process.Close();
return output;
}
Switching these lines to this:
string output = output = process.StandardOutput.ReadToEnd();
process.WaitForExit();
allows to avoid deadlocks. The program seems to be a relatively slow running program due to hard-drive I/O, just give it time and you'll see it complete.
I found this deadlock issue from https://msdn.microsoft.com/en-us/library/system.diagnostics.process.standardoutput(v=vs.110).aspx
Where it states in a code block: "// To avoid deadlocks, always read the output stream first and then wait."
I am trying to restart an application in WPF.
I tried the following:
Process.Start(Application.ExecutablePath);
Process.GetCurrentProcess().Kill();
And it doesn't work because the application is setup as a single instances application.
Then I tired this:
Process.GetCurrentProcess().Kill();
Process.Start(Application.ExecutablePath);
And it doesn't work because once we Kill the process it will not hit line 2
Is there a way to schedule a the .Start so that I don't run into issue #1.
You could launch a secondary application that would then re-launch your main program after the delay. When I wrote a self-updater a few years ago, that was the implementation path that I took. It was just a simple program that took the executable as a command line arg, would sleep for a tenth of a second, then .Start it.
A better implementation path than I took would be to have the newly-launched program wait for the process that launched it to terminate. Waiting the arbitrary length of time could complicate matters. In order to accomplish this, I would probably pass the process ID to the re-launcher so that it would know exactly which process to wait on.
It's not as hard as you think. All you need to do is call the following method, passing in the command line for the restarted instance:
public static void RestartMe(string commandLine)
{
var myId = Process.GetCurrentProcess().Id;
var myPath = Assembly.GetEntryAssembly().CodeBase.Replace("file:///", "");
var systemPath = typeof(object).Assembly.CodeBase.Replace("file:///", "");
var tempPath = Path.GetTempFileName();
File.WriteAllText(tempPath + ".cs", #"
using System;
using System.Diagnostics;
public class App
{
public static void Main(string[] args)
{
try { Process.GetProcessById(" + myId + #").WaitForExit(); } catch {}
Process.Start(""" + myPath + #""", Environment.CommandLine);
}
}");
var compiler = new ProcessStartInfo
{
FileName = Path.Combine(Path.GetDirectoryName(systemPath), "csc.exe"),
Arguments = tempPath + ".cs",
WorkingDirectory = Path.GetDirectoryName(tempPath),
WindowStyle = ProcessWindowStyle.Hidden,
};
var restarter = new ProcessStartInfo
{
FileName = tempPath + ".exe",
Arguments = commandLine,
WindowStyle = ProcessWindowStyle.Hidden,
};
Process.Start(compiler).WaitForExit();
Process.Start(restarter); // No WaitForExit: restarter WaitForExits us instead
File.Delete(tempPath);
File.Delete(tempPath + ".cs");
Environment.Exit(0);
}
How it works: This actually does create another "restarter" program but does it painlessly and automatically. The restarter program has the current process id and the executable filename built right into it. It will always find the compiler because NET Framework version ships with a compatible csc.exe in the same folder as System.dll.