Detect if the application started made another process - c#

The uninstallers from an application normally launch new process and I am trying to make my application know about it so if after the uninstallation, it will execute some tasks.
NSIS for example.
string uninstallstring = "C:\\Program Files (x86)\\Pidgin\uninstall.exe"; // reg.GetValue("UninstallString").ToString();
Process p = new Process()
{
StartInfo = new ProcessStartInfo()
{
FileName = uninstallstring
},
EnableRaisingEvents = true
};
p.Start();
p.WaitForExit();
Debug.WriteLine(p.HasExited);
Debug.WriteLine(p.ExitCode);
// clean up TASKS after uninstall here
// clean clean clean
code above will try uninstall Pidgin. It will Start() and it should wait for the uninstaller to finish its job and show in Debug panel True/False (HasExited) as well as its ExitCode.
The problem there is, after Start() is executed, the "uninstall.exe" launched a new application "Au_.exe" in "C:\Users\Jayson\AppData\Local\Temp\~nsu.tmp" and "uninstall.exe" closed, WaitForExit() has done its job and Debug panel shows
True
0
those are "HasExited" and "ExitCode" but the Pidgin Uninstall is still running which tells me that "uninstall.exe" launched the "Au_.exe" as not his child process.
My question is, how do I make sure that the uninstaller finish its job?

If you know name of application, started by unnistaller, you can try to check existence of his procces use Process.GetProcessesByName("Au_.exe") by timer. When procees appeared and then disappeared you can raise callback.
Also you can read about GetExitCodeProcess

I think there is no general solution here. You rely on external component that doesn't work as expected (normally we expect that uninstall.exe will exit only after complete uninstall and will return corresponding error code, but it doesn't).
There are several workarounds:
When uninstall.exe complete, look for Au_.exe process and wait for its completion.
After uninstall.exe (and may be Au_.exe) check is it really uninstall Pidgin. You can check filesystem or some special registry keys. Probably you expect something before your custom cleanup tasks. If it was not uninstalled properly - repeat or don't clean up (may be show some error or write log message - it depends on your application).
Made your custom uninstaller for Pidgin that will cleanup all necessary resources. Pidgin is open-source, so you can look how its installer works. May be it will be enough to simply modify existing installer.
Don't worry about Au_.exe and just cleanup your resources - may be it's really not a problem for your application.
(if possible) Ask user of your application to uninstall Pidgin. When user did it - make some simple checks (e.g. C:/Program Files/Pidgin is deleted) and do your cleanup.

Related

Run an External Program or Batch File From a C# Xamarin Program

I want to have my C# (Xamarin) program run an EXE or batch (BAT) file. The user will be running my program, and will click on one of several buttons, some of which open Web pages and others of which run external programs. These files will be on the same computer as the one running the main program and don't need greater permissions. The overall program will be in Windows, UWP.
I already have code to pull info from the database saying "the button the user clicked references a program and it's (eg) C:\Tools\MyTool.exe". (Real path more like (C:\Users\Me\source\repos\ProductNameV2\ProductName\ProductName.UWP\Assets\EXE\whatever.exe".) I used a "demo.bat" file containing nothing but echo and pause statements, or references to a built-in Windows program like Notepad or Calc that an ordinary command prompt can recognize without an explicit path (ie. that's part of the recognized system Path). Yes, the real path to the dummy file does exist; I checked. I've also explicitly added files demo.bat and dummy.txt to my C# project.
Here's roughly what I've tried so far to actually run a batch file, or an EXE, or just to try opening a text file. Nothing works.
1)
bool check = await Launcher.CanOpenAsync(#"file:///C:\Tools\demo.bat"); // Returns false.
bool check = await Launcher.CanOpenAsync(#"file:///C:\Tools\dummy.txt"); // Returns true.
await Launcher.OpenAsync(#"file:///C:\Tools\demo.bat") // Seems to do nothing; silently fails.
await Launcher.OpenAsync(#"file:///C:\Tools\dummy.txt") // Same.
2)
Process batchProcess = new Process();
batchProcess.StartInfo.FileName = #"file:///C:\Tools\demo.bat"; // Same result with notepad.exe
batchProcess.StartInfo.WindowStyle = ProcessWindowStyle.Normal;
batchProcess.Start();
batchProcess.WaitForExit();
// Result: "Access is denied" error during Start().
3)
var otherProcessInfo = new ProcessStartInfo(#"file:///C:\Tools\demo.bat")
var otherProcess = Process.Start(otherProcessInfo);
otherProcess.WaitForExit();
otherProcess.Close();
// Result: "The system cannot find the file specified" despite it being the same path as in previous examples.
// Also tried literally using the path C:\Tools\demo.bat, without adding that to the C# project.
// One thing that slightly works is to use:
var otherProcessInfo = new ProcessStartInfo("cmd.exe", "/c echo Hello world!");
// This version opens a window and instantly closes it again. With "/c pause" instead, it opens, saying "press any key to continue".
// Chaining multiple commands with newline or semicolon characters doesn't work as a form of batch file.
So: the only tiny success I've had here is to run cmd.exe, to run a one-line command. I suppose that depending on what the batch file must do, there's some possibility of receiving a string, breaking it into lines, then running cmd.exe using method 3 to call them one at a time. Which is ugly at best.
Is there some better way to do this -- to run a batch file or an EXE from within my program?
EDIT: Yes, I did in fact look at documentation before asking. Why did I use URIs? Because of multiple errors telling me that the simple path strings ("C:\this\that") I was using were in an "Invalid URI format". Using Process.Start("notepad.exe") silently fails, doing nothing. Using a method involving System.Diagnostics.Process (found at How to run external program via a C# program? and yes I saw that before) fails with an error of "Access denied" when using my batch file reference, or silently failing (no window opens) using plain old notepad.exe. I avoided setting Process options that say hide the window.
So to rephrase: Is there a way to make my program run some EXE somewhere on the computer, or to run a batch file that has more than one command in it? What is that way?
Using the data you collected, I was able to run a batch file by doing the following:
var strPathToExeOrBat = System.IO.Path.Combine("C:\\Tools", "demo.bat");
var otherProcessInfo = new ProcessStartInfo("cmd.exe", $"/c call \"{strPathToExeOrBat\"");
var otherProcess = Process.Start(otherProcessInfo);
otherProcess.WaitForExit();
otherProcess.Close();
I also think it would be helpful to review the capabilities of the cmd.exe application.
I found this post to be helpful:
https://stackoverflow.com/questions/515309/what-does-cmd-c-mean#:~:text=%2FC%20Carries%20out%20the%20command%20specified%20by%20the%20string%20and,switches%20by%20typing%20cmd%20%2F%3F%20.
In particular the /k option will leave the window open, if you don't want it to close after running a script.
Thank you very much for your question! It really helped me find the answer to this! (at least for my situation of a .NET MAUI windows app, but MAUI is built off of Xamarin.Forms, so you shouldn't have a problem doing the same thing)
EDIT: Updated to use file path from question and string interpolation with System.IO.Path.Combine for slightly greater cross platform capability

TeamCity build failure because of child process exits with nonzero exit code

I have a TeamCity (8.1) build project which runs fine most of the time. A part of my MSBuild script is, that the built executable should be code-signed, and there the build fails sometimes.
<Target Name="AfterBuild">
<Exec Command="sign.exe /D "$(OutputName)" "$(TargetPath)" "certificate.pfx" password" />
</Target>
The sign.exe is a helper command line tool, which internally calls the signtool.exe from the Microsoft Windows SDK (it detects which version is installed) using hard coded timestamp servers in an iteration, because sometimes, a timestamp server is not reachable.
foreach (var tsServer in TimestampServer)
{
var p = new Process
{
StartInfo =
{
UseShellExecute = false,
RedirectStandardOutput = true,
RedirectStandardError = true,
FileName = signtool,
Arguments = String.Format("sign /f \"{0}\" /p {1} {4} /d \"{3}\" \"{2}\"", cert, pass, file, description ?? Path.GetFileName(file), tsServer)
}
};
p.Start();
p.WaitForExit();
if (p.ExitCode == 0)
{
// signing succeeded
Environment.Exit(0);
}
}
// if all failed issue a nonzero exit code
Environment.Exit(-1);
My custom sign.exe only returns a nonzero exit code if signing with all timestamp servers failed (edit: which not happened yet). But TeamCity marks the build as failed, because it detects, that a child process of my sign.exe exited with a nonzero exit code (edit:) even if a later call to signtool.exe was successful and sign.exe returns a zero exit code.
I know the option, to ignore nonzero exit codes, but I only want to ignore the exit codes of the child processes of my sign tool (edit) not the exit code of my sign.exe, because my tool was written exactly for the purpose to handle this issue.
Is there a way within TeamCity to handle this issue, or do I have an option to modify my custom C# command line tool to not propagate the exit codes of the child processes?
I know the option, to ignore nonzero exit codes, but I only want to ignore the exit codes of the child processes of my sign tool.
Are you sure that is a safe thing to do? It is OK in your environment if signing fails, and yet the build is created?
Is there a way within TeamCity to handle this issue, or do I have an option to modify my custom C# command line tool to not propagate the exit codes of the child processes?
I'm not sure about options within TeamCity, but since sign.exe is under your control, you can always have it return 0 whether or not the child process succeeded.
If all time servers occasionally fail, have you investigated potential causes? Perhaps a temporary internet disruption on your end? You could try pinging something highly reliable outside of your network from sign.exe after all time servers fail, to see if there is a general internet disruption.
It might be worth retrying the entire foreach loop one or more times after a delay, to allow for whatever condition causes the occasional failure to self-correct.

Problem launching a System.Diagnostics.Process under Windows 7

I’m trying to launch an application (Operating System, My Application and the application I want to launch are all 32 bits), from .NET 3.51.
The code that launches the Process is used for other applications, but there’s one that is giving us a headache. If we “double click” on the application’s icon, it works as expected, meaning that it works fine as an application in the computer. Double clicking the .exe directly, also works.
The operating system is Windows 7 32Bits (Home and/or Professional).
Our .NET application is compiled with x86 to avoid problems.
The code that launches “Processes” is located inside a DLL (also 32 bits) made by us, basically it’s a simple DLL that holds some “Common Code” across the board, common methods, functions and stuff we use throughout our code. One of those methods look like this:
public static bool FireUpProcess( Process process, string path, bool enableRaisingEvents,
ProcessWindowStyle windowStyle, string arguments )
{
if ( process != null )
{
try
{
process.StartInfo.FileName = #path;
if ( arguments != null )
{
if ( arguments != String.Empty )
{
process.StartInfo.Arguments = arguments;
}
}
process.StartInfo.WindowStyle = windowStyle;
process.EnableRaisingEvents = enableRaisingEvents;
process.Start();
}
catch
{
try
{
process.Kill();
}
catch ( InvalidOperationException )
{
} // The process is not even created
return false;
}
}
else
{
return false;
}
return true;
}
I don’t know who wrote this method, but it has been working for roughly six years with different applications, therefore I assume it’s “ok”. However, we have a customer with a piece of software that won’t launch when passed through that argument.
The arguments are:
process is a System.Diagnostics.Process created with a simple "new Process();”
path is a full path to the .exe “c:/path/to/my.exe”.
enableRaisingEvents is false
windowStyle is Maximized (but have tried others).
It gives a crappy MessageBox… which I have happily immortalized. It’s in spanish but the translation ought to be easy:
It says:
Application Error
An unexpected exception has occurred for the program (0x0eedfade) at …
Googling that 0x0eedfade gives strange results that look scary, but the truth is, if I go to the .exe that I’m trying to launch and double click it, it works perfectly.
For The Record: If I try to launch other things (I.e.: Notepad.exe, Adobe Acrobat Reader) it works, but Firefox doesn’t open and doesn’t show an error.
This “some work, some doesn’t” behavior leads me to believe that there might be a problem with a Windows 7 security mechanism or similar that I don’t know.
What am I missing or doing wrong?
UPDATE: Ok; I’ve gotten a copy of the software. It’s a messy software but it works. Now that I can debug, I see that the program gives an error when launched with my FireUpProcess method.
As suggested I added the WorkingDirectory code, but here’s the code:
public static bool FireUpProcess(Process process, string path, bool enableRaisingEvents, ProcessWindowStyle windowStyle)
{
if (process != null)
{
try
{
if ( !String.IsNullOrEmpty(#path) )
{
process.StartInfo.FileName = #path;
process.StartInfo.WorkingDirectory = System.IO.Path.GetDirectoryName(#path);
process.StartInfo.WindowStyle = windowStyle;
// Suscribe to the exit notification
process.EnableRaisingEvents = enableRaisingEvents;
// Disable to prevent multiple launchs
Framework.Check.LogWarning("LAUNCHING EXTERNAL DEVICE WITH PATH: " + path);
process.Start(); // HERE The program reports the following:
That means, “The program could not be started because ddip.dll is missing… try reinstalling bla bla”.
The thing is, if I execute the same #path from the command line, the program opens perfectly:
That opens the program. And the same happens if I click on the “shortcut” that it’s located in the “programs” menu. There aren’t any parameters in that shortcut, it’s a simple call to the executable file.
So the question is now: What is the difference between my code and the other methods?
There has got to be something different that causes my process not to start.
Any ideas?
UPDATE AND SOLUTION
I made it work by using one of the below provided answers. Turns out that none directly pointed me to the solution, but they all gave me good ideas here and there.
I added an app manifest to our application (should have had it since the age of vista, don’t know why it wasn’t there in the 1st place). The app manifest I added by using VStudio 2008 add file -> app manifest.
In it, I made sure we have this:
<requestedExecutionLevel level=“asInvoker” uiAccess=“false” />
We don’t need admin or anything like that, but apparently Vista/7 need to know it.
After that was added, the process is correctly launched.
note: UseShellExecute is true by default (as suggested by some), you have to explicitly turn it to false if that’s what you want.
You are not setting the process.StartInfo.WorkingDirectory property. There's plenty of poorly written software out there that assumes the working directory will be the directory in which the EXE is stored. At least add this line:
process.StartInfo.WorkingDirectory = System.IO.Path.GetDirectoryName(#path);
The exception is however rather strange. I'd definitely recommend you tell the customer to update their anti-malware tools.
If the exe has a manifest, you should set UseShellExecute to true on the process object before you call Start. It's not a bad idea in any case.
As Kate Gregory pointed out, if you want to "emulate" the user double clicking on the icon, you have to set UseShellExecute to true. Setting this flags make the code use a totally different path, using the underlying windows ShellExecute function.
Now, I will add to this, that if you're running on a UAC-equipped Windows (Vista, 7, 2008, ...) you maybe should also try to use the runas verb as explained here and here.
With .NET, that would be:
if (System.Environment.OSVersion.Version.Major >= 6) // UAC's around...
{
processStartInfo.Verb = "runas";
}
I've had similar problems in the past. I resolved it by executing the cmd app as follows:
public static bool FireUpProcess(Process process, string path, bool enableRaisingEvents, ProcessWindowStyle windowStyle)
{
//if path contains " ", surround it with quotes.
//add /c and the path as parameters to the cmd process.
//Any other parameters can be added after the path.
ProcessStartInfo psi = new ProcessStartInfo("cmd", "/c" + path ));
psi.WorkingDirectory = System.IO.Path.GetDirectoryName(#path);
psi.WindowStyle = windowStyle;
// Suscribe to the exit notification
process.EnableRaisingEvents = enableRaisingEvents;
// Disable to prevent multiple launchs
Framework.Check.LogWarning("LAUNCHING EXTERNAL DEVICE WITH PATH: " + path);
process.Start(); ...}
If it is possible I would try to use Process Monitor from Sysinternals. When you start it up you can deselect Registry and Network Activity on the toolbar (the 5 icons on the right side). Then you only see Process and Disk activity. Since it looks like a file not found problem you should use the Filter dialog (6. icon from the left) select Process Name from the Drop down list (Architecture is the default) and enter your failing executable name. This will greatly limit the captured output so you can see what is going on. Then start the exectuable and check in the Result Column for NAME NOT FOUND result. This are the locations where a file was searched but not found. If you know the offending dll name you can search for it with Ctrl+F as usual to dig it out. Then you can compare the different search paths from your working application and when it was started from your application.
Could it be that the environment variable PATH has a different value inside your process? It could be that adding . (the current directory) helps to fix the dll search path. Or is the application started from a different user account? It could also be the new feature that when an application is installing things into Programm Files but has no rights (only administrator can do this) Windows will redirect the writes into the user profile. This is a secure and transparent way to get more secure. But this could cause e.g. during first application startup some e.g. config file to be deployed into the Administrators Profile when he is running the application not with consent from the UAC dialog. Then other users might also start the application but fail because the additional config file is located in the Administrators profile and not in Program Files as expected for everyone.
I believe Hans Passant is on the right track. In addition to what he said, check to ensure that ddip.dll and the exe are in the same directory. This is not always the case as there are other ways to bind assemblies outside the bin. Namely, the GAC and AssemblyResolve event. Considering your situation I see no reason the GAC is involved. Check the exe's code that is launched for any hooks into the AssemblyResolve event. If it's hooked into you may need to update the implementation to allow another process to launch it.
Because you are getting an exception regarding a missing DLL, I have little confidence in the answers regarding path delimiter issues. Nonetheless, you have the application code, so verify that it references ddip.dll. This will give you a good deal of confidence that you are in fact referencing the correct .exe and therefore it's not just a path delimiter problem with the command prompt (E.G. misinterpreted spaces).

How to programmatically self delete? (C# WinMobile)

How do I programmatically self delete?
C# / .NET Compact Framework 2 / Windows Mobile 6
Please, I don't want to discuss WHY to do it, I just need to know HOW to do it!
Important:
The "second application" approach is NOT an option. (Unless that second application can be "extracted" from running app, but I don't know how to do it!).
No problem in forced reboot, if windows do the trick at startup. (Is it possible? Nice! Show me how!).
Code samples are welcome.
The only way I can think of offhand to delete yourself and leave no trace is to use something already present in the device- namely wceload (the CAB extractor). I'd create a simple CAB file with a custom installer DLL that does a wait and then the delete.
I'd then add the CAB to the app as an embedded resource. When you need to delete you
extract the CAB to the file system
execute wceload.exe with the CAB as a parameter and /noui (or /silent)
Quit your application
The CAB then deletes your file (a named mutex could sync this better than just a Sleep call in the DLL). wceload automatically deletes the CAB (well depending on WinMo version, but there is a switch to force delete if necessary).
It's certainly a hack, but it would provide a "leave no trace" delete. Of course the CAB would probably have to clean it's own installation registry entries as well. Maybe you could just have the install return "failure" to prevent them from being written in the first place.
I am using this code and it works fine
string AppPath = System.IO.Path.GetDirectoryName(Assembly.GetEntryAssembly().Location).ToString() + "\\Uninstaller.exe";
ProcessStartInfo Info = new ProcessStartInfo();
Info.Arguments = "/C choice /C Y /N /D Y /T 0 & Del " + AppPath;
Info.WindowStyle = ProcessWindowStyle.Hidden;
Info.CreateNoWindow = true;
Info.FileName = "cmd.exe";
Process.Start(Info);
I've done this in the past by simply writing a batch file to the file system that will wait a few seconds and then delete the program. You then use Process.Start() to kick off the batch file and immediately call Environment.Exit(). You need to make sure that the batch file waits long enough for your program to close, or it won't work.
Windows can delete files on startup. It can be done by calling MoveFileEx like:
MoveFileEx(szDstFile, NULL, MOVEFILE_DELAY_UNTIL_REBOOT);
I'm not sure if that API is available in Mobile 6 or not. However, what it actually does is create a registry entry in HKEY_LOCAL_MACHINE\System\CurrentControlSet\Control\Session Manager\PendingFileRenameOperations. The key is a REG_MULI_SZ and you just need to give it a value like "szDstFile\0\0". On reboot Windows deletes the file.
As for programmatically rebooting, have a look at this thread on SO.

From C#, open an arbitrary application

Related question [stackoverflow] here.
I'm trying to do the above, but I want to take the process one step further. I want to open an arbitrary file using the default editor for the file type. From that point, I want to allow my user to interact with the file as they would normally, or continue to work in my application. The extension is what happens after the user finishes editing. Is there a way I can capture a close (and ideally save) event from the external application and use that as a trigger to do something else? For my purposes, tracking the closing of the external application would do.
I can do this in the specific case. For example, I can open a Word instance from my application and track the events that interest my application. However, I want to de-couple my application from Word.I want to allow my users to use any document editor of their choice, then manage the storage of the file being edited discretely behind the scenes.
You can do this in a manner similar to the referenced question, but the syntax is slightly different:
System.Diagnostics.Process process = new System.Diagnostics.Process();
process.StartInfo =
new System.Diagnostics.ProcessStartInfo("C:\...\...\myfile.html");
process.Start();
process.WaitForExit(); // this line is the key difference
The WaitForExit() call will block until the other application is closed. You would use this code in a separate thread so that the user can keep using your application in the meantime.
Use the FileSystemWatcher class to watch for changes to the file.
EDIT: You can also handle the Exited event of the Process object to find out when the program is exited. However, note that that won't tell you of the user closes your file but doesn't exit the process. (Which is especially likely in Word).
To listen for file change, you can use the FileSystemWatcher and listen for a change in the last modified date.
You can also monitor the process and check then file when the process close.
I found this useful tip online just now. It seems to be what you are looking for. This article (link broken) has some more detail and useful, to-the-point tips on C# programming.
string filename = "instruction.txt";
System.Diagnostics.ProcessStartInfo psi = new System.Diagnostics.ProcessStartInfo(#filename);
System.Diagnostics.Process rfp = new System.Diagnostics.Process();
rfp = System.Diagnostics.Process.Start(psi);
rfp.WaitForExit(2000);
if (rfp.HasExited)
{
System.IO.File.Delete(filename);
}
//execute other code after the program has closed
MessageBox.ShowDialog("The program is done.");

Categories