by erich at 2013-02-13 09:57:53
I have a script producing a GUI for users that can take a bit of time in a few places because of some looping, but I am able to use the Windows.forms.progressbar GUI element to indicate something is happening and where in the process it is. I also have a few single commands (not loops) that take a bit of time and when run at the PoSh command line will put up a status bar like what you might call yourself in code loops with Write-Progress. Because this script has a GUI interface however, the users don't see this and think something is broken.
Can that Write-Progress status bar information put up by a cmdlet be captured or redirected so that it can be passed to the Windows.forms.progressbar GUI element? If not, what is a good way to inform the user that things are progressing, are not frozen, and they should just be patient?
by DonJ at 2013-02-13 10:33:30
No, Write-Progress can't be captured or redirected. It's drawn directly on the screen by the hosting application. In terms of a better way to inform users... depends a bit on what's happening. You could just display a message, I suppose.
by erich at 2013-02-13 10:54:06
So there is no way to programatically know the progress/status of a cmdlet as it runs? Only visually. That's too bad.
Is there a way I can fake it? Maybe a simultaneous loop that will open a progressbar and asymptotically approach 100% but can be broken out of or recalled when the cmdlet returns? (I swear that is what happens with some Explorer functions and application installers. 🙂 )
If that is a possibility, how do I multi-task without too much overhead? One is just a "please wait" message and the delay in the other is for server processing, not local machine time. I'd hate to start a whole second PowerShell instance just for this as sometimes the delay is shorter than the time it takes to bring up PowerShell. Just as important, can the primary task stop the "useless" one when it's done?
by DonJ at 2013-02-13 13:24:25
No, there's not. Cmdlets are essentially little black boxes. They don't kick out any kind of status information. Unless the cmdlet itself writes a progress bar, you won't even get that much.
And yeah, in some cases you can workaround. Like, suppose you need to copy 100 files. Rather than piping all 100 to Copy-Item, you run a foreach and copy one at a time, so that you can update whatever progress indicator you want. In terms of "breaking out" of it... that's harder. Understand that PowerShell is a procedural language, not an event-driven language. What you're doing is really not a great use-case for PowerShell, honestly. I suppose your GUI cancel button (or whatever) could set a flag, and you check that flag in your foreach loop, and break out of the loop if the flag is set... fairly ham-handed.
PowerShell doesn't "do" multitasking. Again, you're sounding more and more like you need some lovely Visual Studio in your life than PowerShell ;). You can sorta-kinda fake multitasking by starting tasks on background jobs, using Start-Job. You can then poll the job object status until it finishes. You can execute Stop-Job to stop a job... but what that actually will do depends a bit on what the job was running. Jobs are sorta-kinda another whole PowerShell process, so there's no communication between them. You *are* starting a whole PowerShell instance... just not an interactive console. Sorta-kinda. But starting a job is quicker than launching a new console app instance.
Just keep in mind PowerShell wasn't really conceived with this kind of scenario. Me... I'd probably dig out Visual Studio, and write a little VB (or C#) app. That app can easily host PowerShell's engine (not the console app, just the internal engine) and use it to execute commands and scripts and whatnot. You're building your own host app at that point. VB and C# can easily spin off multiple threads. They can get a little spinning-circle progress thingy going on a dialog, wait until a thread finishes execution, and then stop the spinning-thing. Those languages are *designed* for the kind of async processing you're describing.
I'll offer an alternative, sticking with what you're already doing. On your GUI form, just add a PictureBox. Give the PictureBox an animated GIF of a swirly stuff-is-happening graphic. Set the PictureBox to be hidden by default. When you start your process, show the PictureBox. When your process finishes, hide the PictureBox. That way, something visual is "happening," even though there's no code running. You're not showing "progress" per se, but something's happening on-screen. I had a dev friend once who called it the "mesmerizing eye."
by erich at 2013-02-13 14:06:43
I wish I were good enough to whip up some real programming (or had the time to figure it out). I certainly have enough ideas to keep me busy with that. As it is, I can muddle through PowerShell and Batch, and I've modified a few simple VBS, HTA and KiX scripts, but I'm not what I would call a "true scripter". I just happen to do (and enjoy) it more than anyone else in my department.
I think your "mesmerizing eye" is a workable answer for this. The users just need to feel assured it's working for the longer delays (so far no longer than 10-20 seconds).