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. 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. A browser that asks for a hidden desktop without naming one gets a desktop of its own that nothing else will be handed, and browsers naming the same desktop land on it together. Sharing is what naming is for, and that is the whole of the difference between three browsers on one desktop and three browsers on three.
// 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
Naming a desktop is the only way two browsers land on the same one, and a browser that names nothing gets one of its own. That is the whole rule. If you do not want a hidden desktop, do not call the setter at all.
There are two ways in and they do the same thing at different scope. browser.HiddenDesktop is the one desktop that browser is on, or null when it is on the desktop you are looking at, so a workflow holding the browser never has to remember what the desktop was called. 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 that has windows, 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 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 browser windows on a bare background. 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. 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
GPAL
+ $"holding [{browser
browser
browser
// 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. 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 lives until its last window and its last handle are gone, so
// getting rid of one means closing its browsers and then letting go
foreach (IBrowser browser in openBrowsers)
browser
GPAL
// or just the one, when other desktops are still in use
browser
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();