I have more than one instance of Firefox open, multiple tabs in each, and Chrome also running. I wish to be able to find which tab of which browser is running Pandora, for example.
I am working primarily in VBScript, but can tackle PowerShell and might have access to VB.net.
With VBScript you need a third party component to get access to that kind of information. Otherwise your only option is to shell out and parse the output of the tasklist command.
searchTerm = "Pandora"
Set sh = CreateObject("WScript.Shell")
Set ps = sh.Exec("tasklist /v /fo csv")
data = ps.StdOut.ReadAll
searchTerm = "Pandora"
Set re = New RegExp
re.Pattern = "^""(?:chrome|firefox|iexplore|opera)\.exe"""
re.IgnoreCase = True
For Each line In Split(data, vbNewLine)
If re.Test(line) Then
fields = Split(Mid(line, 2, Len(line)-2), """,""")
If InStr(fields(UBound(fields)), searchTerm) > 0 Then
pid = CInt(fields(1))
End If
End If
Next
sh.AppActivate pid
In PowerShell it's a lot easier to do this kind of thing, since Get-Process already provides you with the window titles.
$searchTerm = 'Pandora'
Get-Process | Where-Object { $_.MainWindowTitle -like "*$searchTerm*" }
Related
I have to create a Powershell script that reads a Word file (.docx) and replaces strings by hyperlinks. So far, based on this script, I can replace all occurrences of a string by another string easily. With that script, I can look for a string and replace it by an hyperlink. However, only the first occurrence is replaced.
Here's my understanding of the problem so far :
The first script uses the ReplaceWith and Replace=wdReplaceAll parameters of the Execute function of the Find Interface. The issue is that ReplaceWith expects a String and not an Hyperlink object.
The second script doesn't specify those parameters so it only uses the Find.Execute() function to move the start of the Range object to the found string and then insert a link at that position.
Since I can't replace all occurrences at once, I'd try to iterate through all matches to insert links at their location. But Find.Execute() only returns a Boolean... Now I'm thinking of maybe redefining the range to exclude the found occurrence and looping until the end of the doc, but this feels complicated.
Let's say I've got a Word file with this text :
In other words, each of the articles linked here is an index to
multiple lists on a topic. Some of the linked articles are themselves
lists of lists of lists. This article is also a list of lists.
Here's a bare bone script that replace only the first occurrence of "lists" by a relative link. I'm trying to replace all occurrences of "lists" to the hyperlink $linkPath, but can't find how. Help ?
Add-Type -AssemblyName "Microsoft.Office.Interop.Word"
$wdunits = "Microsoft.Office.Interop.Word.wdunits" -as [type]
$objWord = New-Object -ComObject Word.Application
$objWord.Visible = $false
# Text to find and replace by a link
$findText = "lists"
# Link to file
$linkPath = ".\Untitled.png"
# Source Word (2007+) file
$objDoc = $objWord.Documents.Open([FILE TO READ FROM])
# Resulting file
$saveAs = [FILE TO SAVE TO]
# Set Range to all document content
$range = $objDoc.Content
$range.movestart($wdunits::wdword,$range.start) | Out-Null
# Execute params
$matchCase = $false
$matchWholeWord = $true
$matchWildcards = $false
$matchSoundsLike = $false
$matchAllWordForms = $false
$forward = $true
$wrap = 1
$format = $False
$wdReplaceNone = 0
$wdFindContinue = 1
$wdReplaceAll = 2
# $wordFound is true is $findText is found in $range.
# $range.find.execute modifies the start of the range
$wordFound = $range.find.execute($findText,$matchCase,`
$matchWholeWord,$matchWildCards,$matchSoundsLike,`
$matchAllWordForms,$forward,$wrap)
if($wordFound){
$objDoc.Hyperlinks.Add($range,$linkPath,$null,$null,$findText) | Out-Null
}
$objDoc.SaveAs($saveAs)
$objDoc.Close()
$objWord.Quit()
$rc = [System.Runtime.Interopservices.Marshal]::ReleaseComObject($objWord)
[gc]::Collect()
[gc]::WaitForPendingFinalizers()
Reference
Find Interface
Find.Execute
Range Interface
Hyperlinks Object
As with any dataset you have to loop to hit all items in the data set to take action on specific in the dataset. You are not doing this in your code. In MSWord, you need to walk the document. For example I am show code for deletes, but this just as well could be your replace effort.
Example: VBA for just delete any hyper link
Sub RemoveHyperlinksInDoc()
' You need to delete collection members starting from the end going backwards
With ActiveDocument
For i = .Hyperlinks.Count To 1 Step -1
.Hyperlinks(i).Delete
Next
End With
End Sub
Example PowerShell for delete all hyperlinks
Param
(
[string]$Document = $Word.Documents.Open((Read-Host -Prompt 'Enter the full path to the Word document'))
)
$Word = New-Object -ComObject Word.application
$Hyperlinks = #($Document.Hyperlinks)
$hyperlinks | ForEach { $_.Delete() }
$document.save()
$document.Close()
$word.quit()
Example: PowerShell for delete only image hyperlinks
Param
(
[string]$Document = $Word.Documents.Open((Read-Host -Prompt 'Enter the full path to the Word document'))
)
$Word = New-Object -ComObject Word.application
$Hyperlinks = #($Document.Hyperlinks)
$Hyperlinks | ForEach {
If ($_.Shape) {$_.Delete()}
Else {$_.Name;Write-Warning -Message 'Hyperlink is not a graphic and will not be removed'}
}
$Document.save()
$Document.Close()
$Word.quit()
What's the best way to get the DNS Cache in Visual Basic and returning recently resolved domains? I only need the domains to compare them whit a list.
Function GetDnsCache()
Dim DNSCache As New Process
DNSCache.StartInfo.FileName = "ipconfig"
DNSCache.StartInfo.Arguments = "/displaydns "
DNSCache.StartInfo.UseShellExecute = False
DNSCache.StartInfo.RedirectStandardOutput = True
DNSCache.
DNSCache.Start()
MsgBox(DNSCache.StandardOutput.ReadToEnd())
DNSCache.WaitForExit()
End Function
This is not the cleanest way of doing this and it also takes ages to parse and load.
I would do something like this. It executes ipconfig /displaydns and appends the output to a file. Then, the file is read line by line and displayed wherever you want (I've used a Listbox)
Dim Shell = CreateObject("Wscript.Shell")
Shell.run("cmd /c ipconfig /displaydns >> C:\ipconfig.txt")
Using reader As New IO.StreamReader("C:\ipconfig.txt")
While Not reader.EndOfStream
Dim currentLine As String = reader.ReadLine()
ListBox1.Items.Add(currentLine)
End While
End Using
Should you like to display the info into a MsgBox...
Dim Shell = CreateObject("Wscript.Shell")
Shell.run("cmd /c ipconfig /displaydns >> C:\ipconfig.txt")
Dim reader as As New IO.StreamReader("C:\ipconfig.txt")
MsgBox(reader.ReadToEnd.ToString, MsgBoxStyle.Information)
I'm trying to send a string variable to command prompt using vba excel.
I'm using following code:-
code_name = "xyz.c"
version = "def"
label = "1XDFO"
'open command prompt
Dim oWsc As Object
Set oWsc = CreateObject("WScript.Shell")
Dim oExec As Object
'save config spec in a text file
Set oExec = oWsc.Exec("perl F:\My_work\hello_world.pl code_name version label")
While oExec.Status <> 1 ' Wait for process
Sleep 1000
Wend
While calling perl script at line
"oWsc.Exec("perl F:\My_work\hello_world.pl code_name version label")"
i want to send original contents of string variable code_name,version,label; but its sending the variable names as it is and not the content; Can anyone help me with this?
Thanks in advance.
How can an external script (Perl or any other one) understand that what you are sending are variables and read their content? You have to send just the content:
Set oExec = oWsc.Exec("perl F:\My_work\hello_world.pl " & code_name & " " & version & " " & label)
Clarification 1: VBA or .NET or any other Microsoft language, do not "read variables inside strings", like other languages do, PHP for example.
Clarification 2: even a language capable of reading variables inside strings cannot manage what is being done "outside its framework". If you call a PHP script from an external program, you cannot call it as you are doing now because PHP does not have any control on what the external program does and thus on its variables.
I have a vbscript file that I want to run in my vb.net application. In the application, the script 'must' use a process. The reason is that it's actually being called from a windows process. In order for me to execute the vbscript manually, I have to right-click on the shortcut and select 'run as administrator'.
How can I emulate this using vb.net? Currently the execution works because I tested it with it only creating a text file. Also, I want to assume that the user is in the administrators group and don't want them to have to login each time because it will execute every minute.
My code:
Dim foo As New System.Diagnostics.Process
foo.StartInfo.WorkingDirectory = "c:\"
foo.StartInfo.RedirectStandardOutput = True
foo.StartInfo.FileName = "cmd.exe"
foo.StartInfo.Arguments = "%comspec% /C cscript.exe //B //Nologo C:\aaa\test.vbs"
foo.StartInfo.UseShellExecute = False
foo.StartInfo.CreateNoWindow = True
foo.Start()
foo.WaitForExit()
foo.Dispose()
Thanks.
The class ProcessStartInfo has two properties that can be used to define the user name that will run your script
ProcessStartInfo.UserName
ProcessStartInfo.Password
Please note as from MSDN:
The WorkingDirectory property must be set if UserName and Password are provided. If the property is not set, the default working directory is %SYSTEMROOT%\system32.
The Password property is of type SecureString. This class need a special initialization code like this:
' Of course doing this will render the secure string totally 'insecure'
Dim pass As String = "Password"
Dim passString As SecureString = New SecureString()
For Each c As Char In pass
passString.AppendChar(ch)
Next
So your code could be changed in this way
Dim foo As New System.Diagnostics.Process
foo.StartInfo.WorkingDirectory = "c:\"
foo.StartInfo.RedirectStandardOutput = True
foo.StartInfo.FileName = "cmd.exe"
foo.StartInfo.Arguments = "%comspec% /C cscript.exe //B //Nologo C:\aaa\test.vbs"
foo.StartInfo.UseShellExecute = False
foo.StartInfo.CreateNoWindow = True
foo.StartInfo.UserName = "administrator"
foo.StartInfo.Password = passString
foo.Start()
foo.WaitForExit()
foo.Dispose()
so I've tried Process and starting a cmd.exe and send commands directly to that window. And then picking up the values written to the cmd.exe window.
The code looks like this:
Dim arrServers As ArrayList
Dim s(ListBoxServers.Items.Count) As String
ListBoxServers.Items.CopyTo(s, 0)
arrServers = New ArrayList(s)
Using P As New Process
P.StartInfo.FileName = "cmd.exe"
P.StartInfo.UseShellExecute = False
P.StartInfo.RedirectStandardOutput = True
P.StartInfo.RedirectStandardInput = True
P.Start()
For Each i In arrServers
P.StandardInput.WriteLine("query user " & txtBoxUsername.Text & " /server:" & i)
Next
P.StandardInput.WriteLine("exit")
Output = P.StandardOutput.ReadToEnd()
Trace.WriteLine(Output)
MsgBox(Output)
P.WaitForExit()
End Using
But is looks like it doesn't "press enter" or something. Meaning, I don't get any results from the command. I don't even get a "'command' is not recognized as an internal or external command, operable program or batch file." like you normally get if it doesn't understand the syntax.
Look into the Process class in the System.Diagnostics namespace for running your batch file.
Imagine the following really simple batch file called "hello.bat"
#ECHO OFF
echo Hello
You can call it and see "Hello" by using:
'Will hold the results of the batch
Dim Output As String
'Create a new process object
Using P As New Process()
'Set the script to run
P.StartInfo.FileName = "c:\scripts\hello.bat"
'My script doesn't take argument but this is where you would pass them
P.StartInfo.Arguments = ""
'Required to redirect output, don't both worrying what it means
P.StartInfo.UseShellExecute = False
'Tell the system that you want to see the output
P.StartInfo.RedirectStandardOutput = True
'Start your batch
P.Start()
'Read the entire contents of the outout
Output = P.StandardOutput.ReadToEnd()
'Wait until the batch is done running
P.WaitForExit()
End Using
'Do something with the output
Trace.WriteLine("Batch produced : " & Output)
Edit
Here's a version that doesn't run a batch but instead runs a couple of standard commands. We start by firing up a command shell to pass things to. One thing that sucks is that its hard to run a command, read the output and then run another command. The code below runs two commands back-to-back and dumps the entire result into a string. If you have a need for running a command, processing, running another command, I think you'll have to wire up something to StandardError and look at return codes. Before you do that, make sure you read up on problem with blocking and how other places solve it by wiring threads up such as here. Probably the easier way is to wrap this into a sub and call the sub once for each command.
'Will hold all of the text
Dim Output As String
'Create a new process object
Using P As New Process()
'Set the script to run the standard command shell
P.StartInfo.FileName = "cmd.exe"
'Required to redirect output, don't both worrying what it means
P.StartInfo.UseShellExecute = False
'Tell the system that you want to read/write to it
P.StartInfo.RedirectStandardOutput = True
P.StartInfo.RedirectStandardInput = True
'Start your batch
P.Start()
'Send your various commands
P.StandardInput.WriteLine("dir c:\")
P.StandardInput.WriteLine("ipconfig /all")
'Very important, send the "exit" command otherwise STDOUT will never close the stream
P.StandardInput.WriteLine("exit")
'Read the entire stream
Output = P.StandardOutput.ReadToEnd()
'Wait until the batch is done running
P.WaitForExit()
End Using
'Do something with the output
Trace.WriteLine(Output)
Edit 2
I'm having problems with the "query user" command in general, I can't get it to return anything for usernames with spaces in them even if I enclose the name in quotes. But here's a version that uses "quser" instead which does the exact same thing as far as I know.
'Will hold all of the text
Dim Output As String
'Create a new process object
Using P As New Process()
'Set the script to run the standard command shell
P.StartInfo.FileName = "cmd.exe"
'Required to redirect output, don't both worrying what it means
P.StartInfo.UseShellExecute = False
'Tell the system that you want to read/write to it
P.StartInfo.RedirectStandardOutput = True
P.StartInfo.RedirectStandardInput = True
'Start your batch
P.Start()
'Send your various commands
'Array of servers
Dim arrServers() As String = New String() {"SERVER1", "SERVER2"}
'Loop through array, wrap names with quotes in case they have spaces
For Each S In arrServers
P.StandardInput.WriteLine(String.Format("quser ""{0}"" /SERVER:{1}", Me.txtBoxUsername.Text, S))
Next
'Very important, send the "exit" command otherwise STDOUT will never close the stream
P.StandardInput.WriteLine("exit")
'Read the entire stream
Output = P.StandardOutput.ReadToEnd()
'Wait until the batch is done running
P.WaitForExit()
End Using
'Do something with the output
Trace.WriteLine(Output)
Use a library/class like NDesk's Options for flexible argument handling. If you don't want to use a external component, you'll have to loop over the arguments and process them manually:
For Each arg As String In Environment.GetCommandLineArgs()
Select Case arg
Case "/blah"
' process /blah '
Case "/foo"
' process foo '
Case Else
MsgBox "Unknown argument " + arg " found, aborting.", vbCritical
Environment.Exit(1)
End Select
Next
[I normally don't do VB, so this is just an untested sketch]