I'm trying to automate the use of an application with WinAppDriver and Appium on Windows 10 using C# .Net.
I'm trying to attach to an already running Window and I've been failing to find a way. I've tried many examples on the web, including from the github site for the WinAppDriver.
https://github.com/microsoft/WinAppDriver
I finally found a way to get a handle for the process, but when I try to connect it fails. It claims it's not a top level window.
The specific error is: "OpenQA.Selenium.WebDriverException: 'b2c is not a top level window handle'"
I've tried the "app" entry instead of "appTopLevelWindow", and it did not work.
Here is the code I'm running:
private void grabWindowsProcesses2()
{
int number = 0;
_txtBxOutput.Text = "";
IntPtr myAppTopLevelWindowHandle = new IntPtr();
foreach (Process clsProcess in Process.GetProcesses())
{
number++;
_txtBxOutput.Text += number.ToString() + ") " + clsProcess.ProcessName + "\n";
if (clsProcess.ProcessName.Contains("VisualBoyAdvance"))
{
myAppTopLevelWindowHandle = clsProcess.Handle;
break;
}
}
var appTopLevelWindowHandleHex = myAppTopLevelWindowHandle.ToString("x");
AppiumOptions appCapabilities = new AppiumOptions();
appCapabilities.AddAdditionalCapability("platformName", "Windows");
appCapabilities.AddAdditionalCapability("deviceName", "WindowsPC");
appCapabilities.AddAdditionalCapability("appTopLevelWindow", appTopLevelWindowHandleHex);
/*
* Error I get here is: OpenQA.Selenium.WebDriverException: 'b2c is not a top level window handle'
*/
_visualBoyAdvance = new WindowsDriver<WindowsElement>(new Uri("http://127.0.0.1:4723"), appCapabilities);
}
Does anyone have any ideas about what I'm doing wrong? I'm fairly new to automating a desktop. I don't know, maybe there is a better way.
Related
I am having some trouble with this error in Visual Studio:
An unhandled exception of type 'System.IO.IOException' occurred in mscorlib.dll
Additional information: The process cannot access the file 'C:\Users\aheij\Desktop\KinectOutput\swipe.txt' because it is being used by another process.
What I Have tried:
I have tried using these codes obtained from other solved StackOverflow questions similar to mine to try to solve the problem - but even that didn't seem to work?
Ive tried checking if the file is in use, but with no success.
I also run Visual Studio as administrator.
The file is not read-only.
The folder is not open, and the file is not being used in any other program when executing the code - at least not that I can see/know of.
The code:
So, to add some context to my code: I am writing some quick gesture detection code to the Kinect c# BodyBasics SDK v2 code freely available. This is a helper method that I added, that gets called in when a person is in view. If that person is executing the gesture, the method writes the frame time and gesture name to a text file.
Half the time, when the code does work, the gesture recognition works well. However, the other half of the time, the code breaks/stops because the writing to file bit causes the error.
Below is my code to see if the person is standing in the neutral position - its a bit waffly, so apologies about that. I have commented 'ERROR' where the error is (unsurprisingly):
private void Neutral_stance(VisualStyleElement.Tab.Body body, IReadOnlyDictionary<JointType, Joint> joints, IDictionary<JointType, Point> jointPoints, BodyFrame bf)
{
CameraSpacePoint left_hand = joints[JointType.HandLeft].Position;
CameraSpacePoint left_elbow = joints[JointType.ElbowLeft].Position;
CameraSpacePoint left_shoulder = joints[JointType.ShoulderLeft].Position;
CameraSpacePoint left_hip = joints[JointType.HipLeft].Position;
CameraSpacePoint right_hand = joints[JointType.HandRight].Position;
CameraSpacePoint right_elbow = joints[JointType.ElbowRight].Position;
CameraSpacePoint right_shoulder = joints[JointType.ShoulderRight].Position;
CameraSpacePoint right_hip = joints[JointType.HipRight].Position;
double vertical_error = 0.15;
double shoulderhand_xrange_l = Math.Abs(left_hand.X - left_shoulder.X);
double shoulderhand_xrange_r = Math.Abs(right_hand.X - right_shoulder.X);
if (bf != null)
{
TimeSpan frametime = bf.RelativeTime;
string path_p = #"C:\Users\aheij\Desktop\KinectOutput\Punch.txt"; //write to punch file
string path_s = #"C:\Users\aheij\Desktop\KinectOutput\swipe.txt"; //write to swipe file
if (left_hand.Y < left_elbow.Y)
{
if (right_hand.Y < right_elbow.Y)
{
if (shoulderhand_xrange_l < vertical_error)
{
if (shoulderhand_xrange_r < vertical_error)
{
Gesture_being_done.Text = " Neutral";
File.AppendAllText(path_p, frametime.ToString() + " Neutral" + Environment.NewLine); //ERROR
File.AppendAllText(path_s, frametime.ToString() + " Neutral" + Environment.NewLine); //ERROR
}
}
}
}
else
{
Gesture_being_done.Text = " Unknown";
File.AppendAllText(path_p, frametime.ToString() + " Unknown" + Environment.NewLine); //ERROR
File.AppendAllText(path_s, frametime.ToString() + " Unknown" + Environment.NewLine); //ERROR
}
}
}
Any solutions/ideas/suggestions to point me on the right track would be appreciated. I think that it would be good to use the 'using streamwriter' method as opposed to the method I am using here - but I am not sure how? Any help would be appreciated.
Additonal Info: Using Visual Studio 2015; Using windows 10.
Sidenote: I read somewhere that the Windows Search tool in Windows 10 can interfere and cause problems like this so I need to disable it?
As suggested to me I used the Filestream method & ensured the file was closed after use. But, even this still caused the same error.
Thus, I also got rid of having two file-writing actions in rapid succession of each other. I dont know if this is technically right or even true, but based off of this post here: link, my error could be coming up because I am trying to execute the second 'write to text file' line whilst the previous 'write to text file' line is still executing/writing to that same folder & location - hence the clash? Please someone, correct me if I am wrong.
Either way, this seems to have worked.
See below for my edited/corrected method:
private void Neutral_stance(Body body, IReadOnlyDictionary<JointType, Joint> joints, IDictionary<JointType, Point> jointPoints, BodyFrame bf)
{
//cameraspace point joint stuff here again (see original post for this bit leading up to the if statements.)
if (bf != null)
{
TimeSpan frametime = bf.RelativeTime;
string path_s = #"C:\Users\aheij\Desktop\KinectOutput\swipe.txt";
if (left_hand.Y < left_elbow.Y)
{
if (right_hand.Y < right_elbow.Y)
{
if (shoulderhand_xrange_l < vertical_error)
{
if (shoulderhand_xrange_r < vertical_error)
{
Gesture_being_done.Text = " Neutral";
FileStream fs_s = new FileStream(path_s, FileMode.Append); //swipe
byte[] bdatas = Encoding.Default.GetBytes(frametime.ToString() + " Neutral" + Environment.NewLine);
fs_s.Write(bdatas, 0, bdatas.Length);
fs_s.Close();
}
}
}
}
else
{
Gesture_being_done.Text = " Unknown";
FileStream fs_s = new FileStream(path_s, FileMode.Append);
byte[] bdatas = Encoding.Default.GetBytes(frametime.ToString() + " Unknown" + Environment.NewLine);
fs_s.Write(bdatas, 0, bdatas.Length);
fs_s.Close();
}
}
}
Do let me know if there is any way I can make this more elegant or anything else I should be aware of w.r.t this answer.
The code is based off of the code found here: FileStream Tutorial website
I am working on automation on IE 8 for one application. I am trying to get the second IE window which is opened from first window. But in ShellWindows I am getting only the first window. Herewith the code I am using:
InternetExplorer IE = null;
ShellWindows m_IEFoundBrowsers = new ShellWindowsClass();
foreach (InternetExplorer Browser in m_IEFoundBrowsers)
{
Logger.Log("Hi I am StatusText :" + Browser.StatusText + ", Url name : " + Browser.LocationURL + ", HWND : " + Browser.HWND);
if (Browser.HWND == IEPtr.ToInt32())
{
IE = Browser;
break;
}
}
if (IE == null)
throw new Exception("Error in creating Internet explorer instance");
But it seems I am not getting the second window (Browser.HWND). I have run the same code in other system and its working fine. I was just wondering is there any IE setting which i am missing in my system.
Thanks in advance.
Does this answer help? It sounded like it was the same issue you're running into:
Active tab ignored by InternetExplorer COM object for IE 8
I don't want to use SetForegroundWindow(), sending keyboard keys or similar techniques, because that can cause issues (unexpected behaviour) in my software.
I have tried to find the title using Cheat Engine program (but haven't found anything useful as Google Chrome seems to work "upside-down").
So I went step ahead, using Process Hacker program I have realized that there is a parent (chrome.exe) process with a valid window handle to the current active tab and all other chrome processes are children of it a.k.a. background processes (with invalid window handle).
By browsing deeper into windows of chrome.exe (parent process), I have found the class name of the window handle being "Chrome_WidgetWin_1" and current active tab's title/text.
Here's a picture of Google Chrome's Task Manager.
I'm looking for a function in C# or C or C++ that will take an integer (process ID) and return a string (tab title/text).
static string GetChromeTabTitle(uint processId)
{
// Assuming I call this function with valid process identifier (PID).
// What do I do next, here??
}
The best way I have found is by using the System.Windows.Automation library. It allows interacting with an application (primarily for accessibility purposes), but you can use it for other purposes like getting Chrome tabs.
Note that this will only work when the Chrome windows is not minimized.
The process is not exactly simple, if you want you can look how I did it in my own project, though it's not something you can just copy it paste, you'll find what you need in the ChromeTabsFinder: https://github.com/christianrondeau/GoToWindow/blob/master/GoToWindow.Plugins.ExpandBrowsersTabs/Chrome/ChromeTabsFinder.cs
Here's the code (you'll need the automation librairies):
public IEnumerable<ITab> GetTabsOfWindow(IntPtr hWnd)
{
var cacheRequest = new CacheRequest();
cacheRequest.Add(AutomationElement.NameProperty);
cacheRequest.Add(AutomationElement.LocalizedControlTypeProperty);
cacheRequest.Add(SelectionItemPattern.Pattern);
cacheRequest.Add(SelectionItemPattern.SelectionContainerProperty);
cacheRequest.TreeScope = TreeScope.Element;
AutomationElement tabBarElement;
using (cacheRequest.Activate())
{
var chromeWindow = AutomationElement.FromHandle(hWnd);
var mainElement = chromeWindow.FindFirst(TreeScope.Children, new PropertyCondition(AutomationElement.NameProperty, "Google Chrome"));
if (mainElement == null)
yield break;
tabBarElement = mainElement.FindFirst(TreeScope.Descendants, new PropertyCondition(AutomationElement.LocalizedControlTypeProperty, "tab"));
}
if(tabBarElement == null)
yield break;
var tabElements = tabBarElement.FindAll(TreeScope.Children, new PropertyCondition(AutomationElement.LocalizedControlTypeProperty, "tab item"));
for (var tabIndex = 0; tabIndex < tabElements.Count; tabIndex++)
{
yield return "Tab: " + tabElements[tabIndex].Current.Name + ", Index: " + tabIndex + 1;
}
}
I'll try and simplify the problem I'm having. Firstly, I'd like to do this is C# but I'd be perfectly fine with any solution.
There's a console application running in a CMD window on a machine that spits out a new line of text every so often. This application is extremely unstable and sometimes freezes, but the window is still responsive so Windows has no idea.
I want to be able to read the contents of this screen so I can restart the process if there hasn't been an update within a certain threshold. I can do the logic no problem, but how can I tell if the screen has any new data?
I was thinking of screen shotting the window and comparing A to B. I know how to do the screen shots but I was looking for something a bit more elegant and to be honest, something that's not as resource intensive as taking a screen shot (The system is quite taxed whilst this application is running).
The application is launched via a .bat script which is why it resides in a CMD window (though obviously the process itself is accessible).
Thank you for any help and ideas.
Another way to receive console output is attach to the cmd process:
var poList= Process.GetProcesses().Where(process => process.ProcessName.Contains("cmd"));
var a = poList.First();
//FreeConsole();//if you use console application you must free self console
AttachConsole((uint) a.Id);
var err=Marshal.GetLastWin32Error();
var ptr=GetStdHandle(-11);
SMALL_RECT srctReadRect= new SMALL_RECT
{
Top = 0,Left = 0,Bottom = 1,Right = 79
};
CHAR_INFO[,] chiBuffer = new CHAR_INFO[2,80]; // [2][80];
COORD coordBufSize= new COORD
{
X = 2,Y = 80
};
COORD coordBufCoord = new COORD
{
X = 0,
Y = 0
};
bool fSuccess;
fSuccess = ReadConsoleOutput( ptr,chiBuffer,coordBufSize,chiBuffer coordBufCoord,ref srctReadRect);
all pinvoke functions can be copied form PInvike ConsoleFunctions
Let me know if you need mode detailed information
If you able to start batch file from C# wrapper application, create process via C# app:
Process proc = new Process();
proc.StartInfo = new ProcessStartInfo("d:\\batch.bat")
{
RedirectStandardOutput = true,
UseShellExecute = false
};
bool updated = false;
Timer waitTimer= new Timer(state =>
{
if (!updated)
{
proc.Kill();
return;
}
updated = false;
});
waitTimer.Change(1000, 1000);
proc.Start();
while (! proc.StandardOutput.EndOfStream)
{
var line = proc.StandardOutput.ReadLine();
updated = true;
Console.WriteLine(line);
}
For reasons I can't get into right now, I need to prevent the Adobe Reader window from opening up when I try to print a document. The developer that was working on this before me has the following flags set, although I'm not really sure what they're for -
if (RegistryManager.GetAcrobatVersion() >= 9.0f)
printerArg = "\"" + printerName + "\"";
else
printerArg = printerName;
Process myProc = new Process();
myProc.StartInfo.FileName = fileName;
myProc.StartInfo.Verb = "printto";
myProc.StartInfo.UseShellExecute = true;
myProc.StartInfo.WindowStyle = ProcessWindowStyle.Hidden;
myProc.StartInfo.CreateNoWindow = true;
myProc.StartInfo.Arguments = "\"" + printerName + "\"";
bool result = myProc.Start();
if (myProc.WaitForInputIdle())
{
if (!myProc.HasExited)
{
myProc.WaitForExit(Convert.ToInt32(5000));
myProc.Kill();
}
}
myProc.Close();
Any help is much appreciated!
Thanks,
Teja.
While I can't answer your question specifically, I found that I couldn't do this as Adobe changed Reader I think at version 9 or 10 so that you couldn't supress the print dialog, and the window itself kept coming up anyway, and since my users all had different versions of Reader installed I couldn't get anything consistently working. If you want to try anyway have a look at Reader's API - you need to add a reference to the correct COM library and go from there. Painful.
I ended up dropping Adobe completely by running the PDF through GhostScript. Following is the helper class I created to do the job. gsExePath should be something like C:\Program Files\gs\gs8.71\bin\gswin32c.exe.
public class GSInterface
{
public string GhostScriptExePath { get; private set; }
public GSInterface(string gsExePath)
{
this.GhostScriptExePath = gsExePath;
}
public virtual void CallGhostScript(string[] args)
{
var p = new Process();
p.StartInfo.FileName = this.GhostScriptExePath;
p.StartInfo.Arguments = string.Join(" ", args);
p.StartInfo.WindowStyle = ProcessWindowStyle.Hidden;
p.Start();
p.WaitForExit();
}
public void Print(string filename, string printerName)
{
this.CallGhostScript(new string[] {
"-q",
"-sDEVICE=mswinpr2",
"-sPAPERSIZE=a4",
"-dNOPAUSE",
"-dNoCancel",
"-dBATCH",
"-dDuplex",
string.Format(#"-sOutputFile=""\\spool\{0}""", printerName),
string.Format(#"""{0}""", filename)
});
}
}
The following should print to the Windows default printer:
var printerName = new System.Drawing.Printing.PrinterSettings().PrinterName;
var gs = new GSInterface(gsExePath);
gs.Print(filename, printername);
This may apply only to the computers where I work, or, more broadly, to this version of Adobe (10) on PCs with Windows 7 installed, but I was able to suppress the opening of Acrobat (Pro) each time I printed to .pdf in any other application by doing the following:
Control Panel > (Devices and) Printers > Double click 'Adobe PDF'> Click 'Printer' > 'Printing Preferences' > Uncheck "View Adobe PDF Results" in the 'Adobe PDF Settings' Tab.
Maybe it is helpful for you to use the "/n" parameter of Adobe Reader.
At least your program keeps the focus. But one instance of the reader stays open.
AcroRd32.exe /n /t ...
See: Questions 619158
Regarding the solution Adam proposed (switching off the View Adobe PDF Results from control panel) and Serge Belov commented (on how to do it programmatically), in the registry this change affects the ViewPrintOutput value at 4 positions:
Computer\HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Print\Printers\Adobe PDF\PrinterDriverData
Computer\HKEY_LOCAL_MACHINE\SOFTWARE\WOW6432Node\Microsoft\Windows NT\CurrentVersion\Print\Printers\Adobe PDF\PrinterDriverData
Computer\HKEY_LOCAL_MACHINE\SYSTEM\ControlSet001\Control\Print\Printers\Adobe PDF\PrinterDriverData
Computer\HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Print\Printers\Adobe PDF\PrinterDriverData
However, if you change it from the registry, it doesn't affect the adobe pdf's behaviour, it still shows the resulting pdf after printing
This might be helpful, though
https://groups.google.com/g/microsoft.public.access.reports/c/LWphtEVp6UI