A Win32 desktop object has its own window set and its own input queue. A browser running on one is a real headful browser with real rendering and a real fingerprint, and it is simply not on the screen you are looking at. A desktop holds windows rather than browsers, so GPAL.Application asks for one the same way GPAL.Browser does and Notepad, Excel or a line of business program runs there on the same terms.
That is the difference from headless, which is a different binary that renders differently and is what detection looks for. It is also not a Windows virtual desktop, the Win+Tab kind: those group windows within the one desktop and isolate nothing, so a browser opened there still steals focus and a click still lands wherever focus is.
Desktops are kept by name. Anything that asks for a hidden desktop without naming one gets a desktop of its own that nothing else will be handed, released when that browser or program closes. Anything naming the same desktop lands on it together, and a named desktop stays until GPAL shuts down or you close it. Sharing is what naming is for, and that is the whole of the difference between three programs on one desktop and three programs on three. A browser and a program can share a desktop, which is how a workflow puts a download in front of the application that has to open it.
// no name, so this browser gets a desktop of its own that nothing else is handed
IBrowser own = GPAL
GPAL
// naming one is the only way two browsers land on the same desktop
IBrowser shared = GPAL
// deciding at run time, which is what a workflow with a switch on the form does
IAllowBrowserSettingsOrGoTo building = GPAL
building = true == eachOnItsOwn
? building
: building
IBrowser third = building
// a Windows program asks the same way
// application setter is told, so this one reads WithHiddenDesktop(true)
IApplication editor = GPAL
// and a name puts the program on the same desktop as the browser that downloaded for it
IApplication alongside = GPAL
Naming a desktop is the only way two of them land on the same one, and one that names nothing gets a desktop of its own. That is the whole rule. If you do not want a hidden desktop, do not call the setter at all.
An unnamed desktop is called GPAL.Hidden.
There are two ways in and they do the same thing at different scope. browser.HiddenDesktop is the one desktop that browser is on, and application.HiddenDesktop is the one that program is on, either of them null when the thing is on the desktop you are looking at, so a workflow holding the object never has to remember what the desktop was called. Both answer the same IHiddenDesktop, so Peek, Name, WindowCount and Close read the same on a program as on a browser. GPAL.HiddenDesktop is all of them together, and it is where the operations that belong to the screen rather than to one desktop live: walking every desktop made so far, handing the screen back, and giving up the handles. Peek means the same thing on both, so a peek is a peek whichever you call it on.
Peek puts a desktop on the screen the moment it is called, whether anything has arrived on it yet or not, making it first if it has not been made, for as long as you ask and then puts back the one you started on. Switching is all or nothing: the whole screen becomes that desktop and the keyboard and mouse go with it. There is no Explorer running there, so no taskbar and no Start menu, only the windows you put there on a bare background. A program that expects a shell can notice: a file dialog opens fine, and a right click menu that the shell draws may not. Because there is nothing there to leave by, GPAL puts something there: a small window with a Return button, up before the screen arrives, with Escape registered as a hot key on that desktop so it works whichever browser window has focus. The window names the desktop and counts down the time left before the screen goes back on its own, so you always know how long you have. Every desktop but the last also has a Next button. Return can also be called from any thread, and the screen is handed back automatically if the process ends. Close gives up GPAL's handle on a desktop and leaves whatever is running there running. CloseEverything closes what is on it first and then gives up the handle, which is the one to call when the workflow is finished with the desktop rather than just done looking at it.
// the one this browser is on, and the one this program is on
GPAL
+ $"holding [{browser
browser
browser
editor
// all of them, and the screen itself
int total = GPAL
foreach (string name in GPAL
GPAL
$"[{name}] holds [{GPAL
string failed = GPAL
failed = GPAL
failed = GPAL
if (null != failed)
GPAL
Peek starts the walk and returns straight away, handing back null when the screen switched and a reason when it did not. The walk itself runs on a foreground thread, so a process on its way out waits for the screen to be handed back rather than leaving somebody on a desktop they cannot leave. It refuses to start a second walk while one is running, because the second one would take the hidden desktop currently on screen as the place to return to. Nothing has to end a peek by hand. The clock ends it, the escape window ends it, and the process ending ends it. GPAL.HiddenDesktop.Return() is there for the case none of those cover, such as a button on your own form that cuts a peek short, and GPAL.HiddenDesktop.IsShowing is what tells that button whether to be enabled. Neither belongs in the ordinary path.
A desktop that is not the input desktop has no input queue anybody is typing into, and that sets a hard boundary. SendInput only reaches the desktop the user is on, so a hardware engine cannot drive a browser here; one asked for anyway steps down to the protocol engine of its own family, and the browser says which engine it settled on.
A program is the same boundary with a different way round it: UIAutomation asks a control to do something rather than aiming a mouse at it, so clicking, filling in, selecting a tab and answering a file dialog all work there, and GPAL says once per program that hardware input cannot reach the desktop and UIAutomation is driving instead.
What has no way round it is anything that is only the mouse: a drag, a hardware click asked for by name, and a control that publishes no pattern at all, which GPAL publishes a warning about rather than clicking into nothing.
For the same reason a screen capture is meaningless there, because the desktop is not being rendered to the display at all. GPAL handles that for you: a screenshot of a browser on a hidden desktop is taken through the browser rather than off the screen, the same route a headless browser already uses, so saving images during a hidden desktop run works on all three engines.
// a desktop made for one browser or program goes when it closes
browser
// named desktops are shared, so they stay until GPAL shuts down or you let go
GPAL
// or just the one, when other desktops are still in use
browser
The screenshot route that saves a browser on a hidden desktop is the browser itself, and a Windows program has no equivalent. A capture copies the screen, and the screen is showing the desktop you are on, so a picture taken while a program runs on a hidden desktop is a picture of your own desktop. That makes image matching selectors useless there as well, since they match against that same capture. Read the program through UIAutomation instead, which is what GetGrid and the element properties do, and peek at the desktop when you want to see it with your own eyes.
Peek is not a preview window. For as long as it runs, the machine belongs to that desktop, and anything you were typing goes there instead. Give it a duration you are willing to lose, use the escape window rather than waiting out the clock, and never call it from a workflow that is running unattended.
Showing off some plain text in these paragraphs eligendi laboriosam illo nostrum corporis at libero vel voluptas? Expedita, facere dolores voluptatem ad ab rem assumenda soluta!
Lorem ipsum dolor sit amet consectetur adipisicing elit. Obcaecati, iste distinctio veritatis eligendi laboriosam illo nostrum corporis at libero vel voluptas? Expedita, facere dolores voluptatem ad ab rem assumenda soluta!
Lorem ipsum dolor sit amet consectetur adipisicing elit. Obcaecati, iste distinctio veritatis eligendi laboriosam illo nostrum corporis at libero vel voluptas? Expedita, facere dolores voluptatem ad ab rem assumenda soluta!
Lorem ipsum dolor sit amet consectetur adipisicing elit. Quo veniam mollitia excepturi animi eum illum non libero sapiente provident assumenda, delectus voluptatum nobis sed dolorem adipisci laudantium incidunt. Error, ratione?
Lorem ipsum dolor sit amet consectetur adipisicing elit. Quo veniam mollitia excepturi animi eum illum non libero sapiente provident assumenda, delectus voluptatum nobis sed dolorem adipisci laudantium incidunt. Error, ratione?
Lorem ipsum dolor sit amet consectetur adipisicing elit. Quo veniam mollitia excepturi animi eum illum non libero sapiente provident assumenda, delectus voluptatum nobis sed dolorem adipisci laudantium incidunt. Error, ratione?
Lorem ipsum dolor sit amet consectetur adipisicing elit. Quo veniam mollitia excepturi animi eum illum non libero sapiente provident assumenda, delectus voluptatum nobis sed dolorem adipisci laudantium incidunt. Error, ratione?
Here you can find different accents and emphasis sit amet consectetur adipisicing elit. Obcaecati, iste distinctio veritatis eligendi laboriosam illo nostrum corporis at libero vel voluptas? Expedita, facere dolores voluptatem ad ab rem assumenda soluta!
This is a link and how it could look like bestlinkinthebeautifulworld. Obcaecati, iste distinctio veritatis eligendi laboriosam illo nostrum corporis at libero vel voluptas? Expedita, facere dolores voluptatem ad ab rem assumenda soluta!
Here's just some classic bold text adipisicing elit. Obcaecati, iste distinctio veritatis eligendi laboriosam notBoldSecondbestlinkinthebeautifulworld illo nostrum corporis at libero vel voluptas? Expedita, facere dolores voluptatem ad ab rem assumenda soluta!
Obcaecati, iste distinctio veritatis eligendi laboriosam adipisicing elit illo nostrum corporis at adipisicing elit libero vel voluptas? Expedita, adipisicing facere dolores voluptatem ad ab rem assumenda soluta!
Other cuple of colors in case we want to emphasize several ways adipisicing elit. Obcaecati, iste distinctio veritatis eligendi laboriosam adipisicing elit illo nostrum corporis at voluptatem libero vel voluptas? Expedita, facere dolores voluptatem ad ab rem assumenda soluta!
Lorem ipsum dolor sit amet consectetur adipisicing elit. Obcaecati, iste distinctio veritatis eligendi laboriosam illo nostrum corporis at libero vel voluptas? Expedita, facere dolores voluptatem ad ab rem assumenda soluta! Lorem ipsum dolor, sit amet consectetur adipisicing elit. Quod veniam, quam ad expedita laborum sed at voluptates culpa ipsam ut vel. Ullam temporibus a mollitia quod aliquam ratione exercitationem nesciunt.
Lorem ipsum dolor sit amet consectetur adipisicing elit. Obcaecati, iste distinctio veritatis eligendi laboriosam illo nostrum corporis at libero vel voluptas? Expedita, facere dolores voluptatem ad ab rem assumenda soluta! Lorem ipsum dolor, sit amet consectetur adipisicing elit. Quod veniam, quam ad expedita laborum sed at voluptates culpa ipsam ut vel. Ullam temporibus a mollitia quod aliquam ratione exercitationem nesciunt.
Lorem ipsum dolor sit amet consectetur adipisicing elit. Obcaecati, iste distinctio veritatis eligendi laboriosam illo nostrum corporis at libero vel voluptas? Expedita, facere dolores voluptatem ad ab rem assumenda soluta!
Lorem ipsum dolor sit amet consectetur adipisicing elit. Repudiandae quas consequuntur illo numquam assumenda autem exercitationem distinctio perspiciatis in natus. Eius dicta similique ipsam ipsa minima, nemo quae enim tempore.
GPAL
.CallIfNotFound(GenericCallIfNotFound)
.WithPublishToConsole();
//System.Drawing.Rectangle windowSize = new System.Drawing.Rectangle(10, 10, 1500, 1024);
// NOTE: we have to set browser = before we execute any steps
// this is due to the 'GenericCallIfNotFound' which might throw an exception, and BankScraper will not have the browser set when it calls scraper.Close()
// until the complete fluent line gets executed (meaning every step, meaning browser is not set until everything else succeeds)
browser = GPAL.Browser
.WithBrowserType(Enums.BrowserType.Chrome)
.WithProfileDataDirectory(ChromeProfileLocation)
.WithUseAutomationEngine(AutomationEngine.Selenium)
.WithWindowSize(new System.Drawing.Rectangle(0,0,1920,1080))
.ToGPALObject();