MessageBox in Powershell not brought to front - c#

I'm using an application which allows me to run Powershell scripts on devices within my network and I need to prompt users with a MessageBox.
My script creates the MessageBox fine, but my issue is that it always displays behind my application. I tried a solution online which suggested creating a new form with property Topmost=true and passing it as the first parameter, however it didn't seem to work. Is there anything immediately obvious that I'm doing wrong?
Add-Type -AssemblyName PresentationCore,PresentationFramework
$top = new-Object System.Windows.Forms.Form -property #{Topmost=$true}
$Result = [System.Windows.Forms.MessageBox]::Show($top, $MessageBody,$MessageTitle,$ButtonType,$MessageIcon)

Spawned windows need a parent. If they don't they will fall behind other windows (as you've found).
The following isn't PowerShell related, per se ... but it's what you need to know/do to get where you want to go...
Why isn't MessageBox TopMost?
and possibly
In Powershell how do I bring a messagebox to the foreground, and change focus to a button in the message box

The $this method does NOT work. I do not know where you people get your information or if you even know how to code in Powershell at all, but your are providing misinformation. I tested the $this method and I can absolutely assure you that it does not work in any way shape or form in PowerShell.
Here is the only method that TRULY works in PowerShell:
Add-Type -AssemblyName Microsoft.VisualBasic
[Microsoft.VisualBasic.Interaction]::MsgBox('MyMessage','OKOnly,SystemModal,Information', 'HeaderText')
It is the SystemModal parameter that forces the MsgBox to dominate and always remains on top no matter what.
There is also another documented method, the Wscript.Shell method, that other people claim that it works, see below.
New-Object -ComObject Wscript.Shell
$wshell.Popup("MyMessage",0,"MyHeader",0 + 16 + 4096)
Where first 0 = No timeout, second 0 = OKOnly, 16 = Critical, 4096 = SystemModal
It also do NOT work as I was STILL able to go back to my previous form and make changes to it while the MsgBox was displayed, which is what I do not want.

You don't need to use a [System.Windows.Forms.Form] object, you can do it in 1 line by using the $this variable as the first parameter:
$Result = [System.Windows.Forms.MessageBox]::Show($this, $MessageBody, $MessageTitle, $ButtonType, $MessageIcon)

Related

Why input redirection works by default for sudo and not for passwd? (.NET)

Simple F# script:
#!/usr/bin/env fsharpi
open System
open System.Diagnostics
let Exec (command: string, arguments: string, echo: bool) =
let psi = new System.Diagnostics.ProcessStartInfo(command)
psi.Arguments <- arguments
psi.UseShellExecute <- false
if (echo) then
Console.WriteLine("{0} {1}", command, arguments)
let p = System.Diagnostics.Process.Start(psi)
p.WaitForExit()
p.ExitCode
Exec("sudo", "-k ls -lha /root", true)
Exec("passwd", "myusername", true)
(But actually this is a generic ".NET on Linux" question I guess, because you could write the same in C#)
Why the first Exec() call works and the second one doesn't?
The first one calls sudo properly, and sudo asks me for my password (I can even fail to type it, and it asks it to me again, so it's not related to number of carrier returns employed in stdin).
However, the second one only allows me to write one character and suddenly drops out. It seems to not redirect characters to passwd properly, as the latter doesn't let me write the complete password, and fails saying that passwords don't match.
Can someone explain this?
By default, passwd usually doesn't use standard I/O (mostly for security reasons). You can check the man page for your passwd to see if there's an option to enable stdio - e.g. passwd --stdin. If not, you'll need to use a different utility, like chpasswd.
All in all, nothing to do with .NET. You couldn't do the same thing from your shell either :) Passwords aren't for applications or scripts - they're for users.

Windows Shell Automation InvokeVerb based on number instead of command string

Normally shell automation InvokeVerb only accepts a string command, based on the documentation here: https://msdn.microsoft.com/en-us/library/ms723189(v=vs.85).aspx
However I noticed a cmd line EXE utility that accepts a number instead
http://www.technosys.net/products/utils/pintotaskbar
This is interesting because in Windows 10 "&Pin to taskbar" is removed from shell automation, despite being visible in the UI. ( https://connect.microsoft.com/PowerShell/feedback/details/1609288/pin-to-taskbar-no-longer-working-in-windows-10 )
However with this EXE passing the "number" 5386 as the command works.
I am interested to know what methods can be used to achieve this in either PowerShell/VBScript/.NET language or Win32 C/C++.
I understand this is almost certainly unsupported and likely to break any time as these numbers have changed between OS releases.
An example using the string version in PowerShell
$filepath = "C:\windows\system32\notepad.exe"
$path = Split-Path $FilePath
$shell= New-Object -com "Shell.Application"
$folder=$shell.Namespace($path)
$item = $folder.Parsename((split-path $FilePath -leaf))
$item.InvokeVerb("&Pin to taskbar")
The underlying shell context menu extension system actually works with numeric IDs natively; string verbs are an optional layer on top. However as you observe, command IDs are internal to a context menu extension and are not guaranteed to stay the same between versions (or even between invocations) - the whole point of verbs is that they allow commands to be invoked programatically irrespective of their ID.
In C/C++ you can invoke a command by its ID by casting the numeric ID as a string pointer (the rationale being that since all valid string pointers are higher than 65535, it's "safe" to pass a number as a string pointer as long as it fits into 16 bits, since the receiving API is able to correctly determine whether it is a "real string" or not).
Note that this is different from printing the number as a string, which is not correct. The MAKEINTRESOURCE macro exists for exactly this purpose.
E.g., a command with ID 1234 would be invoked using MAKEINTRESOURCE(1234) as the verb.
I ran into the same problem these days, in a Windows Script Host based script.
Invoking the verb with the localized string didn't work as it used to.
However you can still get the item's "Verbs" collection and then enumerate them until you get the verb you want to invoke. Then you can invoke it using it's "DoIt" method, without the need to pass the verbs name in.
In a WSH JScript it looks like this:
/* workaround for 'item.InvokeVerb("&Delete")' which no longer works */
var verbs = item.Verbs(); // item == return of folder.ParseName(..)
for (var x = 0; x < verbs.Count; x++)
{
var verb = verbs.Item(x);
if (verb.Name == "&Delete") //replace with "&Pin to taskbar"
{
try
{
verb.DoIt();
WSH.Echo ("Deleted:", item.Name);
}
catch(e)
{
WSH.Echo ("Error Deleting:", item.Name + "\r\n\t", e.description, e.number);
}
break;
}
}
}
Likely it also works somehow in PowerShell
ffr:
https://learn.microsoft.com/en-us/windows/win32/shell/folderitem-verbs
https://learn.microsoft.com/en-us/windows/win32/shell/folderitemverb

PowerShell in C# commands

I'm completely new to PowerShell and am confused about some of its command syntax. I've been looking around google; however, some of the syntax I've found such as
Send-Keys "%(ea)Testing{Enter}{F5}"
has errors that I can't seem to get rid of.
Say I open Calculator with
var script = #"
Start-Process calc.exe
";
powerShell.AddScript(script);
powerShell.Invoke();
How does one go about entering in values for fields and sending keystrokes?
That is, enter in a 5, hit the - key, enter in a 6, then hit the ENTER key?
Or even better,
how does one, using PowerShell, enter in some stock symbol (which isn't the default textfield) then search for it by hitting enter? (after opening firefox.exe, navigating to www.yahoo.com)
Thank you for your time
Key SendKeys
BACKSPACE {BACKSPACE}, {BS}, or {BKSP}
BREAK {BREAK}
CAPS LOCK {CAPSLOCK}
DEL or DELETE {DELETE} or {DEL}
DOWN ARROW {DOWN}
END {END}
ENTER {ENTER} or ~
ESC {ESC}
HELP {HELP}
HOME {HOME}
INS or INSERT {INSERT} or {INS}
LEFT ARROW {LEFT}
NUM LOCK {NUMLOCK}
PAGE DOWN {PGDN}
PAGE UP {PGUP}
PRINT SCREEN {PRTSC}
RIGHT ARROW {RIGHT}
SCROLL LOCK {SCROLLLOCK}
TAB {TAB}
UP ARROW {UP}
SHIFT +
CONTROL ^
ALT %
BACKSPACE {BACKSPACE}, {BS}, or {BKSP}
One thing to bear in mind is that it takes a while for the application to start-up, you could be sending your keys before calculator is ready for them.
Try something like:
add-type -AssemblyName microsoft.VisualBasic
add-type -AssemblyName System.Windows.Forms
Calc
start-sleep -Milliseconds 500
[Microsoft.VisualBasic.Interaction]::AppActivate("Calc")
[System.Windows.Forms.SendKeys]::SendWait("1{ADD}1=")
This should get you a quote from google:
$IE=new-object -com internetexplorer.application
$IE.navigate2("https://www.google.co.uk/finance?client=ob&q=NASDAQ:MSFT")
$IE.visible=$true
I can't go to www.yahoo.com as it automatically redirects me to uk.yahoo.com.
I was able however to go to http://finance.yahoo.com and do exactly what you want.
Bit ugly and relies on the page layout but it seems to be working:
add-type -AssemblyName microsoft.VisualBasic
add-type -AssemblyName System.Windows.Forms
& 'C:\Program Files (x86)\Mozilla Firefox\firefox.exe' -url http://finance.yahoo.com
start-sleep 3
[System.Windows.Forms.SendKeys]::SendWait("{TAB}{TAB}{TAB}{TAB}{TAB}{TAB}{TAB}{TAB}{TAB}{TAB}{TAB}{TAB}{TAB}{TAB}{TAB}{TAB}{TAB}{TAB}{TAB}FB{ENTER}")

How do I get back results running a VB Script from C#?

I want to be able to call VB scripts from C#, which is easy enough, but I need to be able to get back the results from these scripts at times. Should I use the method referenced with something to read back, or should I use a different method? I've found a method to getting data back from powershell scripts using Runspaces and Pipelines, but I don't know enough about this technology to know if it will work with VB scripts as well. Ideally, I'd like to do something similar to the powershell method where I can just pass in the contents of the script without needing to reference an external file and get back the results. Can anyone tell me how to do this? Thanks.
Here's a pretty simple way to do it by listening to an event:
Process vbsProcess = new Process();
vbsProcess.StartInfo.FileName = "yourscript.vbs";
vbsProcess.StartInfo.UseShellExecute = false;
vbsProcess.StartInfo.RedirectStandardOutput = true;
vbsProcess.OutputDataReceived += new DataReceivedEventHandler(YourOutputHandler);
vbsProcess.Start();
vbsProcess.WaitForExit();

Watin: Search in children of an element

I want to do a "two step" search using Watin. For example I would like to search for a ul-tag having class "abc". Then I would like to search for a certain li-tag inside that element. The code might look like this:
ie.ElementWithTag("ul", Find.ByClass("abc")).ElementsWithTag("li", Find.ByXYZ());
But Element does not have an ElementWithTag method. Any hint how to do that with Watin?
The authors of Watin told me that this will be supported in the next version. At the moment this can be done using filter and lambdas.
UPDATE
Actually I just saw info that there is ie.ElementWithTag now, look at this question.
So maybe rest of this post won't be that helpful
Don't have ready solution for you, but maybe a starting point.
Some (long) time ago I was writing some automation script for one page. I used powershell but it should be easy to migrate to C# (which I assume you use).
So in this part of script I' searching on page element that has tag input and is named Save Changes.
#getting property from com object::IHTMLDOMAttribute
function getProperty ([System.__ComObject] $obj, [string] $prop)
{
[System.__ComObject].InvokeMember($prop, [System.Reflection.BindingFlags]::GetProperty, $null, $obj, $null)
}
$ie = new-object -com "InternetExplorer.Application";
$ie.visible = $true;
$ie.navigate("http://mytestpage.com");
$doc = $ie.Document;
$saveButton = $null;
$inputElts = $null;
$inputElts = $doc.getElementsByTagName('input')
foreach ($elt in $inputElts)
{
$a = $elt.getAttributeNode('value')
if ($a -and (getProperty $a 'nodeValue') -eq 'Save changes')
{
$saveButton = $elt;
break;
}
}
So If you would replace part in loop that looks for Save Changes property of element (and delete getProperty declaration) with check for proper class than it should do the trick.
Implemented in their new version 2.0.50.11579.
Watin 2.0.50.11579

Categories