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.
Related
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;
}
I'm having an issue related the the BackgroundWorker object and don't have enough experience in C# to understand what's going on. The program is an off-site patching utility. It works but I the UI isn't updating correctly because the processing and UI loop are in the same thread, so I'm looking at moving the processing into a BackgroundWorker.
Since executables are being patched, the patching process checks to make sure they aren't running before files are copied. The problem I'm seeing is with Process.GetProcessesByName. If I run Process.GetProcessesByName in bg_InstallPatch it appears to work fine, if I call it from within the handleLocalRunningProcesses method the "Couldn't get process information from performance counter." exception is thrown, and I can't find any documentation on why that is. Does something similar to running Invoke on a Form need to happen when calling the method?
The highest .NET runtime level I have available is 3.5.
private void handleLocalRunningProcesses(bool killIfFound = true)
{
logger.Debug("Looking up local processes");
String[] filesToUpload = files.Split(',');
foreach (String file in filesToUpload)
{
String[] fileName = file.Split('.');
logger.Debug("Checking " + fileName[0]);
/******** V Exception Throw Here V ********/
foreach (Process proc in Process.GetProcessesByName(fileName[0]))
try
{
int pid = proc.Id;
logger.Info("Process " + pid + " found running for " + file);
if (killIfFound)
try
{
logger.Info("Attempting to kill process " + pid);
proc.Kill();
if (!proc.WaitForExit(TIMEOUT_KILL_IN_MILLIS))
throw new ApplicationException(String.Format(ERROR_PROCESS_RUNNING, pid, "localhost"));
else
logger.Info("Process has been terminated.");
}
catch (Exception e)
{
logger.Error(e.Message, e);
throw new ApplicationException(String.Format(ERROR_PROCESS_RUNNING, pid, "localhost"));
}
}
finally
{
proc.Dispose();
}
}
logger.Debug("Finished looking up local processes");
}
public void bg_InstallPatch(Object sender, DoWorkEventArgs ea)
{
try
{
//..... Other Code .....
if (updateLocal)
{
logger.Info("Starting Local Updates");
/***Testing***/
logger.Debug("2Looking up local processes");
String[] filesToUpload = files.Split(',');
foreach (String file in filesToUpload)
{
String[] fileName = file.Split('.');
logger.Debug("2Checking " + fileName[0]);
/****** This works fine ******/
foreach (Process proc in Process.GetProcessesByName(fileName[0]))
try
{
int pid = proc.Id;
logger.Info("2Process " + pid + " found running for " + file);
}
finally
{
proc.Dispose();
}
}
/******/
handleLocalRunningProcesses(true);
//..... More Code .....
}
//..... More Code .....
}catch (Exception e)
{
logger.Error("Error installing patch", e);
throw e;
}
}
public void installPatch()
{
//..... Unrelated Code ....
logger.Info("Starting patch installation");
BackgroundWorker patcher = new BackgroundWorker();
patcher.DoWork += new DoWorkEventHandler(bg_InstallPatch);
patcher.RunWorkerCompleted += new RunWorkerCompletedEventHandler(bg_ProgressClose);
patcher.RunWorkerAsync(bar);
//..... More Code .....
}
I don't know the why, but I found the cause. The program showing this behavior is required to run on Windows XP under .NET 3.5. The issue no longer presented itself when running on Windows 10 with .NET 4.5.1 installed. A second test on Windows 8.0 with .NET 3.5 installed also worked. Verified that the program continues to fail in a second, unrelated XP environment with .NET 3.5 installed. The same executable was used for all tests.
Even though the program is compiled as a 32-bit executable, it should still be pointed out that XP is 32-bit and both Windows 8 and 10 were 64-bit. Just in case this behavior would present itself in the 32-bit version of newer operating systems, though I doubt it would.
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;
}
I posted about this a little while ago, but I resolved the other issue and ran into one more. I am about to deploy this program to 28 hosting machines so I want to make sure this is working before I do so.
I wrote a little c# NET application that is basically a wrapper for a Java application, when my app starts, the Java app starts, when my app closes, it closes, and so on.
Everything works properly except that when I close my application, the Java application continues to run. When I create the process, I store the Process var in a variable outside of the methods, and then use that when my application goes to shutdown. For whatever reason though it is not terminating the Java application.
class Program
{
private static Process minecraftProcess;
public static void LaunchMinecraft(String file, String memoryValue)
{
String memParams = "-Xmx" + memoryValue + "M" + " -Xms" + memoryValue + "M ";
String args = memParams + "-jar " + file + " nogui";
ProcessStartInfo processInfo = new ProcessStartInfo("java.exe", args);
processInfo.CreateNoWindow = true;
processInfo.UseShellExecute = false;
try
{
//using (Process minecraftProcess = Process.Start(processInfo))
using (minecraftProcess = Process.Start(processInfo))
{
minecraftProcess.WaitForExit();
}
}
catch
{
// Log Error
}
}
static void Main(string[] args)
{
Arguments CommandLine = new Arguments(args);
// Hook ProcessExit Event
AppDomain.CurrentDomain.ProcessExit += new EventHandler(Current_ProcessExit);
if (CommandLine["file"] != null && CommandLine["memory"] != null)
{
// Launch the Application (Command Line Parameters)
LaunchMinecraft(CommandLine["file"], CommandLine["memory"]);
}
else
{
// Launch the Application (Default Parameters)
LaunchMinecraft("minecraft_server.jar", "1024");
}
}
static void Current_ProcessExit(object sender, EventArgs e)
{
System.Threading.Thread.Sleep(10000);
// If we have an active Minecraft Service, Shut it down
if (minecraftProcess != null)
{
minecraftProcess.Kill();
}
}
}
You can't Sleep in a ProcessExit handler.
The documentation states:
The total execution time of all
ProcessExit event handlers is limited,
just as the total execution time of
all finalizers is limited at process
shutdown. The default is two seconds.
An unmanaged host can change this
execution time by calling the
ICLRPolicyManager::SetTimeout method
with the OPR_ProcessExit enumeration
value.
Nevermind, I just realized the minecraftProcess variable is static.
Don't know if you did not solve this issue by yourself but:
You should be aware that there are Start methods for instances (returning bool) and static (returning a object).
You should not use using with something other than using-local variables!
Just this should work fine:
minecraftProcess = Process.Start(processInfo)
minecraftProcess.WaitForExit();