I have developed a launcher that updates/run our software in other companies enviroment. Most of the time the company approves the launcher but forgets about the actual application.
Currently im using Process.Start(procStart) to start the application, but that silently fails if applocker blocks the application.
So i was wondering if someone reliable way of detecting if applocker is active, and when it blocks my application( So i can provide a proper error message).
When the error happens my application seems idle to the user, a memory dump shows this:
Code note:
There is no exception handling or suppression of exceptions. If the launcher crashes i would expect to see it in the eventlog.
Added code:
private void StartzzzDesktop(int value)
{
var rel = Settings.zzzDesktopStore.GetReleaseInfo(Settings.ConnectionDetails.zzzDesktopID);
var proc = CreateProccess(rel);
if (proc == null)
{
Settings.LastError = zzzLauncherError.FatelErrorStartzzz;
Settings.EventManager.TriggerEvent(zzzDesktopStatus.FatalError);
return;
}
Logger.Log(EventLogEntryType.Information, $"Started zzz desktop and got PID {proc.Id} from {rel.GenerateExtrationPath()}");
Settings.EventManager.TriggerEventSync(zzzDesktopStatus.DeleteOldReleases);
Settings.EventManager.TriggerEvent(zzzDesktopStatus.ReleaseBackgroundWorkers);
GC.Collect();
var remoteStatus = new GetRemotezzzWebStatus();
while (!proc.HasExited)
{
Thread.Sleep(1000);
if(!remoteStatus.IsRemoteVersionCompatible())
{
proc.Kill();
Logger.Log(EventLogEntryType.Information, $"Detected that the remote website is no longer compatible with current runnign version, and we are killing desktop.");
}
}
if(proc.ExitCode != 0)
{
Settings.zzzDesktopStore.Delete(rel);
Logger.Log(EventLogEntryType.Warning, $"zzz exited with a none zero exit code ({proc.ExitCode}), the local cached installation will be deleted");
}
else
Logger.Log(EventLogEntryType.Information, $"zzz exited in a normal way with exitcode {proc.ExitCode}, running for {(DateTime.Now - proc.StartTime).ToString()} ");
CloseDown();
}
internal Process CreateProccess(zzzDesktopInfo release)
{
release = GetReleaseInfo(release.ID);
string pathzzzExe = Path.Combine(release.GenerateExtrationPath(), "zzz.exe");
var verifyStatus = UtilsVerifyFile.Verify(pathzzzExe);
if ( !File.Exists(pathzzzExe) || !verifyStatus.Verified)
{
Logger.Log(EventLogEntryType.Error, "Found zzz.exe in temp folder, but the certificate did not pass verification");
foreach (var logentry in verifyStatus.Logs)
Logger.Log(EventLogEntryType.Error, "Certificate verification log: " + logentry);
MarkDatabaseForPurge();
return null;
}
// Removed enterprise spesific code.
var procStart = new ProcessStartInfo();
procStart.FileName = pathzzzExe;
if (Settings.ConnectionDetails.zzzLoginToken != Guid.Empty )
{
procStart.Arguments = "/RefreshToken:" + Settings.ConnectionDetails.zzzLoginToken.ToString();
}
var process = Process.Start(procStart);
return process;
}
Related
I have a windows form and I make the following call to open chrome up with a URL:
string uri = Uri.EscapeUriString(URL.ToString());
Process process = Process.Start("chrome", uri + " --new-window");
return process.Id;
The problem is that I get the error: "Process with an id of "xxxxxxx" is not running" when I call the following:
procsChrome = Process.GetProcessById(processID);
The processID is the one returned in the previous code snippet. Now, this WORKS when I have NO CHROME WINDOW OPEN.
But if I have another chrome window open, I get this error. I monitored the processes in Task Manager and when no chrome is open, a new process with the the process id returned from the code snippet 1 is created.
When one or more chrome windows are already open, I get a process id from code snippet 1, but the newly opened chrome window is running under some other id. Why is this happening?
Edit 2:
Here is the method I get the error on. The parameter int processID is the process.Id returned above from code snippet 1.
public static string GetCodeFromURL(int processID)
{
Process procsChrome = null;
try
{
if (processID == -1)
{
Console.WriteLine("-1 returned as ID");
return null;
}
procsChrome = Process.GetProcessById(processID); //I GET ERROR HERE
// the chrome process must have a window
if (procsChrome.MainWindowHandle == IntPtr.Zero)
MessageBox.Show("Process failed");
// to find the tabs we first need to locate something reliable - the 'New Tab' button
AutomationElement root = AutomationElement.FromHandle(procsChrome.MainWindowHandle);
var SearchBar = root.FindFirst(TreeScope.Descendants, new PropertyCondition(AutomationElement.NameProperty, "Address and search bar"));
if (SearchBar != null)
{
bool noCode = false;
while (noCode == false)
{
string fullURL = (string)SearchBar.GetCurrentPropertyValue(ValuePatternIdentifiers.ValueProperty);
if (!(fullURL.Contains("code") && fullURL.Contains("state")))
{
if (procsChrome.HasExited)
{
Console.WriteLine("Process failed. User closed browser.");
procsChrome.Close();
noCode = true;
}
}
else
{
string stateToken = HttpUtility.ParseQueryString(fullURL.Substring(new[] { 0, fullURL.IndexOf('?') }.Max())).Get("state");
if (stateToken.Equals("296bc9a0-a2a2-4a57-be1a-d0e2fd9bb601"))
{
noCode = true;
string code = HttpUtility.ParseQueryString(fullURL.Substring(new[] { 0, fullURL.IndexOf('?') }.Max())).Get("code");
procsChrome.CloseMainWindow();
procsChrome.Close();
return code;
}
}
}
}
}
catch (Exception exception)
{
Console.WriteLine("An exception occured on getting the URL. Please try again. The exception is: " + exception.ToString());
return null;
}
return null;
}
Thanks.
Chrome seems to behave in this way by default, creating anothing process and closing the original one, which means you end up with an incorrect Process ID. And when you have multiple Chrome processes with the same website/title you end up having no control over it.
As some suggested using the parameter "--incognito" will solve it, whoever this would also create other unwanted behavioural issues.
I've looked at the Chrome parameters list and tried a few, using the parameter "--no-service-autorun" seems to have solved the issue.
Not sure why chrome is behaving like that. Can you try to have your code launch chrome in incognito mode?
Using the test code below i was able to launch 2 distinct incognito processes each with their own process id. Note: these launched in the same browser window as 2 tabs.
I think using --incognito will get you out of trouble.
public class LaunchChromeProof
{
private readonly ITestOutputHelper _output;
public LaunchChromeProof(ITestOutputHelper output)
{
_output = output;
}
[Fact]
public void CanLaunchChromeIcognito_AndFetchProcesses_ByID()
{
var chromePath = #"C:\Program Files (x86)\Google\Chrome\Application\chrome.exe";
var chromeProcess = Process.Start(chromePath, "www.google.com --incognito");
Assert.NotEqual(0, chromeProcess.Id);
//launch a second session
var chrome2ndProcess = Process.Start(chromePath, "www.google.com --incognito");
Assert.NotEqual(0, chrome2ndProcess.Id);
_output.WriteLine($"Session 1 ProcessID: {chromeProcess.Id}");
_output.WriteLine($"Session 2 ProcessID: {chrome2ndProcess.Id}");
//check we can fetch the correct process by the process id for each session
var fetchProcess1 = Process.GetProcessById(chromeProcess.Id);
Assert.Equal(chromeProcess.Id, fetchProcess1.Id);
var fetchProcess2 = Process.GetProcessById(chrome2ndProcess.Id);
Assert.Equal(chrome2ndProcess.Id, fetchProcess2.Id);
_output.WriteLine($"Fetched Session 1 Process: {fetchProcess1.Id}");
_output.WriteLine($"Fetched Session 2 Process: {fetchProcess2.Id}");
}
This question already has answers here:
How to check if process is idle? C#
(2 answers)
Closed 4 years ago.
I have several programs that are already released. I want to create new program to read the released running programs - wheather they are idle or not for about 5 minutes. If they are in the idle condition, I want to terminate them.
IDLE means the user not use mouse and keyboard or no process in that program.
As an example, these are my several released programs:
pgrA.exe - Running - Idle 3 Minutes
pgrB.exe - Stopped
pgrC.exe - Running - Idle 7 Minutes
pgrD.exe - Running - not Idle
pgrE.exe - Running - Idle 11 Minutes
My program to terminate idle programs (on single PC):
IdleReader.exe (will terminate pgrC.exe and pgrE.exe)
that all running in 1 PC.
this some codes of the new program to terminate idle Program:
private void refresh_PrgList()
{
using (var con = new SqlConnection(ConfigurationManager.AppSettings["ConnectionStr"]))
using (var cmd = con.CreateCommand())
{
con.Open();
cmd.CommandText = "select ProgramID, ProgramName from MKTPrograms";
var reader = cmd.ExecuteReader();
var yy = false;
dgvPrgList.Rows.Clear();
while (reader.Read())
{
yy = false;
foreach (var xx in Process.GetProcesses())
if (xx.ProcessName.Replace(".exe", string.Empty).ToUpper() == reader[0].ToString().Replace(" ", string.Empty).ToUpper())
{
//----- I want to Detect the Idle Program here -----//
//--------------------------------------------------//
dgvPrgList.Rows.Add(new object[] { reader[0].ToString(), reader[0].ToString(), "Running", xx.StartTime, System.DateTime.Now - xx.StartTime });
yy = true;
}
if (yy == false)
dgvPrgList.Rows.Add(new object[] { reader[0].ToString(), reader[1].ToString(), "Stopped", "" });
}
for (int x = 1 ; x < dgvPrgList.Columns.Count ; x++ )
{
dgvPrgList.Columns[x-1].AutoSizeMode = DataGridViewAutoSizeColumnMode.DisplayedCells;
}
}
}
Thanks,
Sure, there are tons of ways to do that.
Look at the processing time (UserProcessorTime,TotalProcessorTime), store it and compare it; you'd get a good feeling about "idle" after some time.
Then just call Kill on the System.Diagnostics.Process class to terminate it.
In .net one can do:
var processName= "yourApp";
if (Process.GetProcessesByName(processName) == null)
Assert.Inconclusive("Skipped: {processName} is not running");
The above code is a unit test, and it can be used to get a specific process, you can find the name you are looking for in your resource monitor:
To see if an application is installed one would simulate the call to start that application, you'd do that like this:
/// <summary>
/// Determines whether the specified application executable is installed.
/// </summary>
/// <param name="name">The command line name.</param>
/// <returns><c>true</c> if the specified name is installed; otherwise, <c>false</c>.</returns>
public static bool IsInstalled(string name)
{
using (var key = Registry.ClassesRoot.OpenSubKey($"{name}\\shell\\open\\command"))
return key != null;
}
One can check if an application is having the right state by looking at the main window. you do this by getting the process as shown before
what I do to see if the application has started but the user is still at his login is:
if (MyAppProcess.MainWindowTitle.Contains("Login"))
return true;
I use:
if (!TCP.IsListing(login.ServerName, login.Port))
Assert.Inconclusive($"Skipped: TWS not accepting connection port {login.Port}");
To see if a method is accepting connections without actually creating a connection, no all applications like it when you just open a socket port, some crash, some corrupt.
The code that does that is listed below, only works locally, you can't do this remote. I use the host as my servers have more than 1 IP and failing to listen will cause my application to fire up a fallback instance and update the DNS server. Easy to create an application failover cluster without having to have enterprise licenses for all your multi-socket multi-core servers ;-)
public static bool IsListing(string hostUri, int portNumber, int millisecondTimeOut=500)
{
try
{
var info = new ProcessStartInfo() {
Arguments = "-a -p TCP",
CreateNoWindow=false,
FileName="netstat",
WindowStyle= ProcessWindowStyle.Hidden,
UseShellExecute = false,
RedirectStandardOutput = true
};
using (var p = Process.Start(info))
using (StreamReader reader = p.StandardOutput)
{
while (!reader.EndOfStream)
{
var line = reader.ReadLine();
if (line.Contains($":{portNumber}"))
{
return true;
}
}
}
return false;
}
catch (SocketException)
{
return false;
}
}
You can see if a host (remote or local) can be reached using a ping. You can do that using:
public static async Task<bool> IsOnline(string hostUri)
{
try
{
object token = Guid.NewGuid().ToString();
using (var p = new Ping())
{
var result= await p.SendPingAsync(hostNameOrAddress: hostUri, timeout: 1000);
return result.Status == IPStatus.Success;
}
}
catch (Exception)
{
return false;
}
}
I am working on a project in c# that using threading to initialize multiple calls to xcopy to copy user directories from one workstation to a network location.
When I run the program in debug mode, sometimes if I have a break-point BEFORE the program hits the calls to XCopy, and I stop the debugging with the stop button (in VS 2010), the program will then proceed to call the XCopy function, even though I stopped it from reaching the calls in the code. If I stop inside the foreach loop does the debugger continue to do the other foreach instances?
I know it sounds crazy, but has anyone else ever experienced this, or might you offer some suggestions that would correct this from happening?
A second issue with this is that when I run it from my Win7 machine accessing an XP machine in Firefox, the osInfo is correct, but when my boss runs it on his Win7 machine in IE, it doesn't work. It makes sense to me that the lines:
System.OperatingSystem osInfo = System.Environment.OSVersion;
if (dir_base.Exists && (osInfo.Platform == System.PlatformID.Win32NT)) //XP
Should be pulling the system that is running the code, not the network location's operating system type, but the if statement results in true when I run it and false when he does...
Here is my code:
public static void BuildSources_Targets(string Source, string str_Target )
{
//XP:
string str_basePath = Path.Combine(Source, "Documents and Settings");
var dir_base = new DirectoryInfo(str_basePath);
System.OperatingSystem osInfo = System.Environment.OSVersion;
if (dir_base.Exists && (osInfo.Platform == System.PlatformID.Win32NT)) //XP
{
foreach (var dir in dir_base.GetFileSystemInfos())
{
switch (dir.ToString())
{
case "administrator":
case "Administrator":
case "Default User":
case "All Users":
//Do nothing
break;
default:
string str_dir = dir.ToString();
//Handle XP App Data
//str_baseAndDirsPath = Path.Combine(dir_base.ToString(), str_dir, Environment.SpecialFolder.ApplicationData.ToString());
string str_baseAndDirsPath = Path.Combine(dir_base.ToString(), str_dir, "Application Data");
DirectoryInfo dir_baseAndDirs = new DirectoryInfo(str_baseAndDirsPath);
if (dir_baseAndDirs.Exists)
{
string str_Destination = Path.Combine(str_Target, str_dir, "Application Data");
ProcessXcopy(str_baseAndDirsPath, str_Destination);
}
//Handle XP Documents
str_baseAndDirsPath = Path.Combine(dir_base.ToString(), str_dir, "My Documents");
dir_baseAndDirs = new DirectoryInfo(str_baseAndDirsPath);
if (dir_baseAndDirs.Exists)
{
string str_Destination = Path.Combine(str_Target, str_dir, str_dir + " Documents");
ProcessXcopy(str_baseAndDirsPath, str_Destination);
}
break;
} //end of switch
} //end of foreach
} //end of dir_base.exists
//it continues from here...but that's enough to illustrate my problem...
I need to queue approximately 20 installations that are fully unattended (Using a C# winform application). Each installation has its own INI file (that is manually created) that contains the proper information on what arguments each installer requires for this procedure (read in before that program is executed). I'm running into issues with many application that when the setup.exe is executed the process closes immediately and launches its MSI (if applicable), causing my procedure to carry out with the next installation assuming that the first is complete. I have read similar problems snooping around the web, but no real solution on the issue... (some workarounds included using a batch file with the /Wait option which should have kept the setup.exe in memory until its MSI has completed). The setup.exe must be launched due to the fact that they contain bootstrappers.
What options do i have to resolve this dilemma?
Here is some sample code that demonstrates the procedure:
foreach (ListViewItem itm in this.lstSoftwares.Items)
{
try
{
if (itm.Checked)
{
lblStatus.Text = "Status: Installing " + current.ToString() + " of " + count.ToString();
string InstallPath = Path.Combine(Application.StartupPath, "Software",
itm.Text, itm.Tag.ToString());
string CommandLine = itm.SubItems[1].Text;
Process process = new Process();
process.StartInfo.FileName = InstallPath;
process.StartInfo.Arguments = CommandLine;
process.StartInfo.WindowStyle = ProcessWindowStyle.Normal;
process.Start();
process.WaitForExit();
this.lstSoftwares.Items[i].SubItems[2].Text = "Complete";
current++;
}
Update
right after waitforexit() i'm using a loop that checks if the msiexec is running:
private bool MSIRunning()
{
try
{
using (var mutex = Mutex.OpenExisting(#"Global\_MSIExecute"))
{
return true;
}
}
catch (Exception)
{
return false;
}
}
this is a hack in my opionion, but doing the trick so far...
Querying the MSI Mutex after process.start in a loop (check if Mutex is running every 3 seconds, if not return and proceed with next install) seemed to solve the problem (Noted above).
Already answered, but I have a slightly more robust implementation of the MSI mutex check:
public bool IsMsiExecFree(TimeSpan maxWaitTime)
{
_logger.Info(#"Waiting up to {0}s for Global\_MSIExecute mutex to become free...", maxWaitTime.TotalSeconds);
// The _MSIExecute mutex is used by the MSI installer service to serialize installations
// and prevent multiple MSI based installations happening at the same time.
// For more info: http://msdn.microsoft.com/en-us/library/aa372909(VS.85).aspx
const string installerServiceMutexName = "Global\\_MSIExecute";
Mutex msiExecuteMutex = null;
var isMsiExecFree = false;
try
{
msiExecuteMutex = Mutex.OpenExisting(installerServiceMutexName,
MutexRights.Synchronize);
isMsiExecFree = msiExecuteMutex.WaitOne(maxWaitTime, false);
}
catch (WaitHandleCannotBeOpenedException)
{
// Mutex doesn't exist, do nothing
isMsiExecFree = true;
}
catch (ObjectDisposedException)
{
// Mutex was disposed between opening it and attempting to wait on it, do nothing
isMsiExecFree = true;
}
finally
{
if (msiExecuteMutex != null && isMsiExecFree)
msiExecuteMutex.ReleaseMutex();
}
_logger.Info(#"Global\_MSIExecute mutex is free, or {0}s has elapsed.", maxWaitTime.TotalSeconds);
return isMsiExecFree;
}
For a project I have to start an application in C#, rip out the AutomationElement tree related to the process, and then close the application and output the tree. I'm doing this by opening the application using Process.Start. Then I'm finding the AutomationElements related to the spawned process and walking the tree using a combination of TreeWalker and AutomationElement's FindFirst and FindAll methods.
This runs fine on my computer and runs correctly using NUnit locally. It also runs on the other people in my groups computers. The problem is that it never runs on our central testing server that's running Hudson. After some hours of debugging, I had a test on Hudson start the application and then print the first level of the AutomationTree. On my computer, this prints all of the windows I have on my desktop. On Hudson, this only prints the Desktop.
Thinking there might be multiple desktops, I tried using TreeWalker's GetNextSibling function on the RootElement. It still only reported one desktop.
Here's the code I'm using to start a process.
public bool connect(string[] args)
{
if (this.process != null) {
Console.WriteLine("ERROR: Process already connected");
return false;
}
if (!File.Exists(sApplicationPath)) {
Console.WriteLine(sApplicationPath + " does not exist");
return false;
}
// Turn the command arguments into a single string
string arguments = "";
foreach (string arg in args) {
arguments += arg + " ";
}
try {
// Start the application
ProcessStartInfo processStartInfo =
new ProcessStartInfo(sApplicationPath);
processStartInfo.Arguments = arguments;
this.process = Process.Start(processStartInfo);
// Must be a positive integer (non-zero)
if ( !( iInitialDelay > 0 ) ) {
Console.WriteLine("Invalid initial delay. " +
"Defaulting to 5 seconds.");
this.iInitialDelay = 5000;
}
Thread.Sleep(this.iInitialDelay);
} catch (Exception ex) {
Console.WriteLine("WGApplication.connect: " + ex.Message);
return false;
}
// Check if the process still exists
try {
/** This part does not return an error, so I think that means the process exists and is started */
Process check = Process.GetProcessById(process.Id);
} catch (ArgumentException ex) {
Console.WriteLine("The process expired before connection was complete");
Console.WriteLine("Make sure the process is not open anywhere else");
Console.WriteLine("and that it is able to execute on the host machine.");
return false;
}
// Check if the base automation element exists to verify open
AutomationElement rootWindow =
AutomationElement.RootElement.FindChildProcessById(process.Id);
/** This part returns null, so it can't find the window associated with this process id */
if (this.process == null) {
return false;
} else if (rootWindow == null) {
// A root window with this process id has not been found
Console.WriteLine("Cannot find the root window of the created " +
"process. Unknown error.");
return false;
} else {
// Everything is good to go
return true;
}
}
sApplicationPath is set to the absolute path of the executable. iInitialDelay is a delay to make sure the application has time to start. I'm running this on 'C:\Windows\System32\notepad.exe' on Windows Vista SP2 and compiling it with the v3.5 C# compiler.
FindChildProcessById is defined as follows:
public static AutomationElement FindChildProcessById(
this AutomationElement element, int processId)
{
var result = element.FindChildByCondition(
new PropertyCondition(AutomationElement.ProcessIdProperty,
processId));
return result;
}
Remember that this compiles and works on my computer. My test program on Hudson said that the RootElement had no children at all.
So I start the application, confirm it exists, and then I can't find any windows associated with the process. I can't find any windows associated with anything except the desktop.
Is this a problem with Hudson? Does Hudson work in some specific way that this code wouldn't work on it? Is it a problem with my code? The Hudson server is running on a Windows Server 2003 computer. Any help would be appreciated. I know this is a very specific problem which is a reason why I can't find any solutions online.
Is Hudson running as a service? If so, it may not have the necessary rights to show windows.