I'm trying to automate an application using TestStack/White API (Which is based on Microsoft's UI Automation library).
The problem is the following:
At a certain point of automation, I have to deal with an "Dialog" window, which looks to be a separate process, if i look at "Windows Task Manager". But no matter how i try to access the "Dialog Window" (Class, ID, Text, ControlType, etc.) I'm not able to access it.
You can find the UISpy image and code below...
Using UISpy - Dialog Information
using (var DISCLAIMER_App = Application.Attach(#"PathToExecutable"))
using (var DISCLAIMER_Window = DISCLAIMER_App.GetWindow(SearchCriteria.ByClassName("#32770"), InitializeOption.NoCache))
{
var IAccept_button = DISCLAIMER_Window.Get<Button>(SearchCriteria.ByText("I accept"));
IAccept_button.Click();
}
# I've tried also Application.Launch, Application.AttachOrLaunch.
# I also looked to be sure that the Dialog window is a separated process and doesn't belong to any parent window.
Any suggestions?
Found the Solution, had to use "ProcessStartInfo()" and pass the return data to "Application.AttachOrLaunch()":
var psi = new ProcessStartInfo(#"PathToExecutable");
using (var DISCLAIMER_App = Application.AttachOrLaunch(psi))
Source: http://techqa.info/programming/tag/white?after=24806697
Related
I am attempting to control two browser windows via selenium using c# and a single chromedriver. The reason being that I need to share session details accross browser windows.
The code that I have tried and failed with is below;
var options = new ChromeOptions();
options.AddArguments("chrome.switches", "--disable-extensions --disable-extensions-file-access-check --disable-extensions-http-throttling --disable-infobars --enable-automation ");
options.AddUserProfilePreference("credentials_enable_service", false);
options.AddUserProfilePreference("profile.password_manager_enabled", false);
options.PageLoadStrategy = PageLoadStrategy.Default;
ChromeDriverService service = ChromeDriverService.CreateDefaultService();
service.HideCommandPromptWindow = true;
var Driver = new ChromeDriver(service, options);
//THIS WILL OPEN A NEW WINDOW. BUT BECAUSE IT IS A NEW DRIVER DOES NOT WORK FOR SHARING SESSION DETAILS.
//var TestDriver = new ChromeDriver(service, options);
//TestDriver.Manage().Window.Maximize();
//THIS JUST OPENS UP A NEW TAB. NOT A NEW WINDOW (IT WOULD SEEM MOST DOCUMENTATION SUGGESTS THAT IT SHOULD)
IJavaScriptExecutor jscript = Driver as IJavaScriptExecutor;
jscript.ExecuteScript("window.open();", "google.com.au");
//TRY USING THE SEND KEYS TECHNIQUE. NOTHING HAPPENS
var test = Driver.FindElement(By.TagName("html"));
test.SendKeys(Keys.Control + "n");
test.SendKeys(Keys.Control + "t");
//TRY AGAIN USING THE SEND KEYS TECHNIQUE USING A DIFFERENT TAG. NOTHING HAPPENS
var blah = Driver.FindElements(By.TagName("body"));
blah[0].SendKeys(Keys.Control + "t");
//TRY USING ACTIONS. NOTHING HAPPENS
Actions action = new Actions(Driver);
action.SendKeys(OpenQA.Selenium.Keys.Control + "n");
action.Build().Perform();
I may resort to AutoIt to open a browser if I have to, but one more dependency is not what I need. Documentation everywhere around the web seems to suggest than all the options I tried above should work...I suspect it may be a chromedriver issue of some kind.
Any ideas on how to achieve my goal would be greatly appreciated
UPDATE.
Arnons answer below lead me to the solution. If you are in a similar situation the best thing to do is just open up the browser console (from developers tools) and experiment with javascript until you get what you want. Then just execute that. In the end executing the following code has worked for me.
IJavaScriptExecutor jscript = Driver as IJavaScriptExecutor;
jscript.ExecuteScript("window.open('https://www.bing.com.au','_blank','toolbar = 0, location = 0, menubar = 0')");
The other alternative was to use Autoit, which I also got working, much easier than I did figuring out the javascript. But one less dependency is best :)
UPDATE2.
Further complications arise with trying to control the window as an independent browser window. I believe any new window created from a parent window, has the same process id (at least my testing has indicated so), and for all intense and purpose is treated as a tab in the selinium driver. I therefore conclude that certain things are just not possible (for example relocating the child browser window on the screen).
Your first attempt using ExecuteJavaScript was very close, but In order for it to open a new window instead of new tab, you should add the following arguments: `"_blank", "toolbar=0,location=0,menubar=0" to it.
See this question for more details.
I should have read the question better, here is my solution. Ended up using this for selecting windows that popped up after clicking a button but should work with swapping between windows.
//---- Setup Handles ----
//Create a Handle to come back to window 1
string currentHandle = driver.CurrentWindowHandle;
//Creates a target handle for window 2
string popupWindowHandle = wait.Until<string>((d) =>
{
string foundHandle = null;
// Subtract out the list of known handles. In the case of a single
// popup, the newHandles list will only have one value.
List<string> newHandles = driver.WindowHandles.Except(originalHandles).ToList();
if (newHandles.Count > 0)
{
foundHandle = newHandles[0];
}
return foundHandle;
});
//Now you can use these next 2 lines to continuously swap
//Swaps to window 2
driver.SwitchTo().Window(popupWindowHandle);
// Do stuff here in second window
//Swap back to window 1
driver.SwitchTo().Window(currentHandle);
// Do stuff here in first window
You need to explicitly tell Selenium which tab you wish to interact with, which in this case would be;
driver.SwitchTo().Window(driver.WindowHandles.Last());
I'm trying to capture the path of selected files and folders from the standard OpenFileDialog window created by another application.
I have seen that is possible to perform this task with windows explorer:
IntPtr handle = GetOpenFileDialogHwnd();
ArrayList selected = new ArrayList();
var shell = new Shell32.Shell();
foreach(SHDocVw.InternetExplorer window in shell.Windows())
{
if (window.HWND == (int)handle)
{
Shell32.FolderItems items = ((Shell32.IShellFolderViewDual2)window.Document).SelectedItems();
foreach(Shell32.FolderItem item in items)
{
selected.Add(item.Path);
}
}
}
However, the SHDocVw.ShellWindows () method does not return the opened openFileDialog hwnd. As windows explorer is very similar to OpenFileDialog, I imagine there is some way to do a cast having the hwnd of OpenFileDialog for the Shell32.IShellFolderViewDual2 interface like:
var view = Shell32.ShellFolderViewDual2.FromHwnd(hwnd);
Is there any alternative way?
The goal is simple, make a log of files used in standard OpenFileDialog windows. Workable in Windows 7, 8, 10.
I know, it seems a very very very strange thing.
Edit:
Inspect.exe give me hope:
An open file dialog is not a shell window so it won't show up in the ShellWindows list.
You can send the undocumented WM_GETISHELLBROWSER (WM_USER+7) message to the dialog window, but the returned IShellBrowser pointer is only valid inside the same process. Using it in another process would cause access violation.
Once you got IShellBrowser you can get other interfaces like IShellView or IFolderView2. For selection you want to use IFolderView2::GetSelection.
It is possible to inject a proxy DLL into the target process to control the file dialog, but you can't write the DLL in C#.
I'm trying to extract text from a Creo 2.0 Window Title. The text will be used to create a folder titled the same as the part number that is opened and in the Window Title. The issue I have is that I can iterate through, and find all the Window Titles of open applications using process.MainWindowTitle, but for some reason, Creo doesn't have a Main Window Title. It also doesn't have the text using any other "process." functions. I figure that the information has to be somewhere if it's in the title bar like other normal programs, but I'm just not going at it the right way. Is there another process using C# that I can use to try and accomplish this?
Let me know if I need to give any other information. Thank you for the help!
It may not necessarily be available via the Process object. Explorer.exe, for example, has the current folder name in the title bar but this is not the MainWindowTitle. Another option is to use UI Automation to inspect the UI and report the window's title. Depending on which version of .Net you are targeting, you can reference UiaComWrapper (which has more information and better perf, I believe) or UIAutomationClient and UIAutomationTypes. A short sample that prints all window titles:
using System;
using System.Diagnostics;
using System.Windows.Automation;
static void Main(string[] args)
{
var windows = AutomationElement.RootElement.FindAll(TreeScope.Descendants,
new PropertyCondition(AutomationElement.ControlTypeProperty, ControlType.Window));
foreach(AutomationElement window in windows)
{
var props = window.Current;
var proc = Process.GetProcessById(props.ProcessId);
Console.WriteLine("{0} {1} {2}", props.ProcessId, proc.ProcessName, props.Name);
}
}
Note that you need to run this elevated if you want to get information on elevated processes.
I've found a very nice tutorial and i am trying to understand something that is not in this tutorial (because the tut itself works fine)
http://www.codeproject.com/Articles/9163/File-Rating-a-practical-example-of-shell-extension
When you look at applications like WinRar, TortoiseSVN, Antivirus-apps and many more, there is an icon next to the Shell Extension Item.
I would like to know how this is done. (Programmatically with C#)
Adding a separator works, adding a submenu works and click+action also works, but i'm struggling with the icon. This cannot be so hard. Can somebody help me?
And please don't say that Microsoft doesn't longer support this in .NET 4.0, because it is not guaranteed and therefore they don't supply samplecode. If all those other apps can do it, then it is possible.
Please supply me some sample code, some tutorials or maybe even a working piece of code.
Please have a look at the following article, it uses .NET 4.0 it to create Windows Shell Extensions using the SharpShell nuget package.
NET Shell Extensions - Shell Context Menus
Using this library, you can set the image directly while creating the contextmenustrip as shown below
protected override ContextMenuStrip CreateMenu()
{
// Create the menu strip.
var menu = new ContextMenuStrip();
// Create a 'count lines' item.
var itemCountLines = new ToolStripMenuItem
{
Text = "Count Lines...",
Image = Properties.Resources.CountLines
};
// When we click, we'll count the lines.
itemCountLines.Click += (sender, args) => CountLines();
// Add the item to the context menu.
menu.Items.Add(itemCountLines);
// Return the menu.
return menu;
}
You only have to add to the following registry key: HKEY_LOCAL_MACHINE\SOFTWARE\Classes*\shellex\ContextMenuHandlers
and here is the code:
string TimeStamp = DateTime.Now.ToString("dd-MM-yyyy");
string key = "HKEY_LOCAL_MACHINE\\SOFTWARE\\Classes\\*\\shellex\\ContextMenuHandlers\\Winrar";
string valueName = "MyWinrar";
Microsoft.Win32.Registry.SetValue(key, valueName, HERE WHAT YOU WANT TO START, Microsoft.Win32.RegistryValueKind.String);
i hope it works for you!
All the apps you listed use COM and unmanaged code to create overlay icon handlers. There is even a special project TortoiseOverlays that provides a common library for drawing icons for TortoiceCSV, TortoiseSVN and TortoiseGIT. You can take a look at it's source code to find out how it is done. If you want to draw similar icons, you should probably just reuse it.
Using .Net for this type of extensions is not recommended, because when multiple extensions, built against different .Net versions would attempt to load in explorer process, they will crash the explorer.
I would like to automate an SAP GUI window using the C# language. I am able to do it in VBScript but code reuse is horrible. Besides Id like to use threading instead of having 80 or more processes running. Where can I find any documentation and samples of how to do this? Here is the code I am working with. Basically, the problem I am facing is - how do I make a connection to SAP GUI then create an SAP GUI on the fly then start making transactions and entering text in some fields.
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using White.Core.Factory;
using White.Core.UIItems.Finders;
using White.Core.InputDevices;
using System.Threading;
using System.Diagnostics;
using SAP.Connector;
using SAP;
namespace SAP_Automation
{
class Program
{
public static void Main(string[] args)
{
string ExeSourceFile = #"C:\Program Files\SAP\SapSetup\setup\SAL\SapLogon.s8l";
White.Core.Application _application;
White.Core.UIItems.WindowItems.Window _mainWindow;
var c = SAP.Connector.Connection.GetConnection("**");
var c = new SAPConnection("ASHOST=*; GWHOST=*; GWSERV=*; ASHOST=*; SYSNR=00;USER=user; PASSWD=**;");
c.Open();
}
}
}
}
As you can see I can create a connection but I dont know how to create a session to the GUI and start entering text in fields. Any examples and samples would be appreciated.
This might be necro-threading but I was in a similar situation where I work. We needed SAP GUI Automation for testing purposes that could integrate with the rest of our homegrown automation platform written in C#. I helped create a proposal for one solution that took advantage of a SAP provided library for GUI automation that could be used as the basis for an automation layer for SAP.
Does the following file exist on your SAP file installation? x:\Program Files\SAP\FrontEnd\SAPGui\sapfewse.ocx?
If so, add it to Visual Studio (or whatever IDE you're using) as a reference. It is basically a class library which contains a bunch of SAP specific objects that will allow you to interact with. It is very effective because it exposes most of what you need from the SAP GUI. We discovered in other attempts that a lot of the objects in SAP were not available.
This is an early proof of concept I did. Start SAP with a connection string, enter credentials, navigate to a transaction code.
using System;
using System.Collections.Generic;
using System.Collections;
using System.Linq;
using System.Text;
using SAPFEWSELib;
namespace SAPGuiAutomated
{
//created a class for the SAP app, connection, and session objects as well as for common methods.
public class SAPActive
{
public static GuiApplication SapGuiApp { get; set; }
public static GuiConnection SapConnection { get; set; }
public static GuiSession SapSession { get; set; }
public static void openSap(string env)
{
SAPActive.SapGuiApp = new GuiApplication();
string connectString = null;
if (env.ToUpper().Equals("DEFAULT"))
{
connectString = "1.0 Test ERP (DEFAULT)";
}
else
{
connectString = env;
}
SAPActive.SapConnection = SAPActive.SapGuiApp.OpenConnection(connectString, Sync: true); //creates connection
SAPActive.SapSession = (GuiSession)SAPActive.SapConnection.Sessions.Item(0); //creates the Gui session off the connection you made
}
public void login(string myclient, string mylogin, string mypass, string mylang)
{
GuiTextField client = (GuiTextField)SAPActive.SapSession.ActiveWindow.FindByName("RSYST-MANDT", "GuiTextField");
GuiTextField login = (GuiTextField)SAPActive.SapSession.ActiveWindow.FindByName("RSYST-BNAME", "GuiTextField");
GuiTextField pass = (GuiTextField)SAPActive.SapSession.ActiveWindow.FindByName("RSYST-BCODE", "GuiPasswordField");
GuiTextField language = (GuiTextField)SAPActive.SapSession.ActiveWindow.FindByName("RSYST-LANGU", "GuiTextField");
client.SetFocus();
client.text = myclient;
login.SetFocus();
login.Text = mylogin;
pass.SetFocus();
pass.Text = mypass;
language.SetFocus();
language.Text = mylang;
//Press the green checkmark button which is about the same as the enter key
GuiButton btn = (GuiButton)SapSession.FindById("/app/con[0]/ses[0]/wnd[0]/tbar[0]/btn[0]");
btn.SetFocus();
btn.Press();
}
}
//--------------------------//
//main method somewhere else
public static void Main(string[] args)
{
SAPActive.openSAP("my connection string");
SAPActive.login("10", "jdoe", "password", "EN");
SAPActive.SapSession.StartTransaction("VA03");
}
You're right there is not a lot of documentation on this subject. Below are a few sources that helped me get started
-Original source of our plan
http://scn.sap.com/thread/1729689
-Documentation on the API (For VB and javascript but the general rules and objects are identical). Definitely read the portion on the SAP GUI Runtime hierarchy. It'll answer a lot of questions.
http://www.synactive.com/download/sap%20gui%20scripting/sap%20gui%20scripting%20api.pdf
It is very important here to understand what UI Automation can do and what its limitations are. It was designed to automate a user interface's capabilities. You can click buttons, enter text in a textbox, move windows, etcetera, whatever a user can do using the mouse and keyboard.
What it can not do is bridge the tall wall that the operating system puts up between processes. A wall that prevents a process from accessing the memory of another process. This is a very important security and safety feature. It for one prevents a process from accessing data that should be private to a process. Like a password. And for another it stops a crashing process from affecting other processes that run on the machine. You can kill a process with Task Manager and everything keeps motoring along happily as though nothing happened.
A consequence of this is that creating a SAPConnection object in your program is a connection that only your program can use. There is no mechanism to somehow pass this object to another process with UI Automation. At best you could use the data you retrieve from the connection to affect what buttons you click.
The kind of process interop that would allow sharing data between processes is well supported in .NET. Low-level approaches are socket and named pipes, high-level are Remoting and WCF. Older programs have COM Automation support, Office is a good example of that. That however requires two to tango, both programs must be written to take advantage of it.
So if you are trying to automate an existing SAP application and this app does not otherwise explicitly support automation, the kind that an Office program supports, then you are pretty much stuck with just filling text boxes and clicking buttons.
You can automate any kind of application (browser, desktop, java, etc) with UiPath.
Here's a tutorial on how to automate data entry, menu navigation and screen scraping on SAP.
You can
use it from code (SDK). It has a tool that auto-generates C# code
create and run workflows (visual automation) directly from UiPath Studio.
Here's a sample of the C# auto-generated code:
// Attach window menu
UiNode wnd3 = UiFactory.Instance.NewUiNode().FromSelector("<wnd app='sap business one.exe' cls='#32768' idx='1' />");
// Click 'Business Pa...' menu
UiNode uiClickBusinessPamenu_3 = wnd3.FindFirst(UiFindScope.UI_FIND_DESCENDANTS, "<ctrl name='Business Partners' role='popup menu' /><ctrl automationid='2561' />");
uiClickBusinessPamenu_3.Click(88, 9, UiClickType.UI_CLICK_SINGLE, UiMouseButton.UI_BTN_LEFT, UiInputMethod.UI_HARDWARE_EVENTS);
// Attach window 'SAP Business'
UiNode wnd4 = UiFactory.Instance.NewUiNode().FromSelector("<wnd app='sap business one.exe' cls='TMFrameClass' title='SAP Business One 9.0 - OEC Computers' />");
// Click 'Add' button
UiNode uiClickAddbutton_4 = wnd4.FindFirst(UiFindScope.UI_FIND_DESCENDANTS, "<wnd cls='ToolbarWindow32' title='View' /><ctrl name='View' role='tool bar' /><ctrl name='Add' role='push button' />");
uiClickAddbutton_4.Click(13, 24, UiClickType.UI_CLICK_SINGLE, UiMouseButton.UI_BTN_LEFT, UiInputMethod.UI_HARDWARE_EVENTS);
Here's how workflow automation of SAP Business One menus, buttons or typing looks like:
And finally the SDK documentation is located here... in case you don't want to use workflows.
Note: I work at UiPath. You should also try other automation tools like Automation Anywhere, WinAutomation, Jacada, Selenium, Ranorex use them side by side and choose the one that suits better your needs.