Powershell, URL shortcut - powershell

I've got a problem and i need your help. I'm trying to do a shortcut from the active URL. I tried a few things and got to this.
Param([switch]$Full, [switch]$Location, [switch]$Content)
$urls = (New-Object -ComObject Shell.Application).Windows() |
Where-Object {$_.LocationUrl -match "(^https?://.+)|(^ftp://)"} |
Where-Object {$_.LocationUrl}
if($Full)
{
$urls
}
elseif($Location)
{
$urls | select Location*
}
elseif($Content)
{
$urls | ForEach-Object {
$ie.LocationName;
$ie.LocationUrl;
$_.Document.body.innerText
}
}
else
{
$urls | ForEach-Object {$_.LocationUrl}
}
$Shortcut = $WshShell.CreateShortcut("E:\Powershell\Ziel\short.lnk")
$Shortcut.TargetPath = "$urls"
$Shortcut.Save()
But i get an shortcut which makes no sense. What do i do wrong? I'm happy about any suggestion.
I tried now doing it like this:
Param([switch]$Full, [switch]$Location, [switch]$Content)
$urls = (New-Object -ComObject Shell.Application).Windows() |
Where-Object {$_.LocationUrl -match "(^https?://.+)|(^ftp://)"} |
Where-Object {$_.LocationUrl}
if($Full)
{
$urls
}
elseif($Location)
{
$urls | select Location*
}
elseif($Content)
{
$urls | ForEach-Object {
$ie.LocationName;
$ie.LocationUrl;
$_.Document.body.innerText
}
}
else
{
$urls | ForEach-Object {$_.LocationUrl}
}
$url = $urls | ForEach-Object {$_.LocationUrl} | select -First 1
$Shortcut = $WshShell.CreateShortcut("E:\Powershell\Ziel\short.lnk")
$Shortcut.TargetPath = $url
$Shortcut.Save()
But no it tells me that "$Shortcut = $WshShell.CreateShortcut("E:\Powershell\Ziel\short.lnk")" has the value NULL. I mean, how is that even possible. I don't get it. Please help.

This one is wrong... (this is not a valid url, it just an array of objects)
$Shortcut.TargetPath = "$urls"
You need to select one of the url's first, for example:
$url = $urls | ForEach-Object {$_.LocationUrl} | select -First 1
Then:
$Shortcut = $WshShell.CreateShortcut("E:\Powershell\Ziel\short.lnk")
$Shortcut.TargetPath = $url
$Shortcut.Save()
if you want to create a url for each of the URL's Array, then you can use
foreach, like this:
foreach ($url in $URLs)
{
$UrlName = $url.LocationName.Substring(0,8)
$Link = $url.LocationUrl
$Shortcut = $WshShell.CreateShortcut("E:\Powershell\Ziel\$UrlName.lnk")
$Shortcut.TargetPath = $Link
$Shortcut.Save()
}

Related

Improving the speed of Get-FileMetaData

I'm currently using the below script taken from scriptingguys.com (all credit to them, I just added the bottom 2 lines.) That takes a directory and pulls the file path and comments field from the meta data of the files. Currently the script take's a little over 1.5 minutes to fully run. Is there anyway to speed this up or use a different method to get this data?
I am using this script at the start of some software I have written and 1.5+ minutes is too long for the script to complete. Any thoughts/comments?
Function Get-FileMetaData
{
Param([string[]]$folder)
foreach($sFolder in $folder)
{
$a = 0
$objShell = New-Object -ComObject Shell.Application
$objFolder = $objShell.namespace($sFolder)
foreach ($File in $objFolder.items())
{
$FileMetaData = New-Object PSOBJECT
for ($a ; $a -le 266; $a++)
{
if($objFolder.getDetailsOf($File, $a))
{
$hash += #{$($objFolder.getDetailsOf($objFolder.items, $a)) =
$($objFolder.getDetailsOf($File, $a)) }
$FileMetaData | Add-Member $hash
$hash.clear()
} #end if
} #end for
$a=0
$FileMetaData
} #end foreach $file
} #end foreach $sfolder
} #end Get-FileMetaData
$fileMetaData = Get-FileMetaData -folder "C:\Pics" | select 'Name', 'Path', 'Comments' | Sort-Object 'Name'
$fileMetaData | select 'Name', 'Path', 'Comments' | Export-CSV "C:\SCRIPTS\TestDirectory.txt" -encoding Utf8 -NoTypeInformation
Solved by wOxxOm, thanks for your help! Running the below and now working.
Function Get-FileMetaData(
[string[]]$folders,
[string[]]$properties
) {
$shellApp = New-Object -ComObject Shell.Application
$supportsOrdered = $PSVersionTable.PSVersion.Major -ge 3
$hash = if ($supportsOrdered) { [ordered]#{} } else { #{} }
# walk the folders and get the properties by index found above
$folders | ForEach {
$shellFolder = $shellApp.namespace($_)
# get all headers and find their indexes
$allProps = #{}
foreach ($index in 0..266) {
$allProps[$shellFolder.getDetailsOf($shellFolder.items, $index)] = $index
}
$shellFolder.items() | ForEach {
$file = $_
$hash.Clear()
foreach ($prop in $properties) {
if (($index = $allProps[$prop]) -ne $null) {
if ($value = $shellFolder.getDetailsOf($file, $index)) {
$hash[$prop] = $value
}
}
}
if ($supportsOrdered) {
[PSCustomObject]$hash
} else {
Select $properties -inputObject (
New-Object PSObject -Property $hash
)
}
}
}
}
Get-FileMetaData -folders 'C:\PICS' -properties Name, Path, Comments | Sort-Object Name |
select Name, Path, Comments | Export-Csv 'C:\Scripts\test.txt' -encoding UTF8 -NoTypeInformation
getDetailsOf is slow, and your code needlessly invokes it 267 times for each file when you only need it for 3 properties.
Collect the property names just once at the start of the function, don't do it on every file
Add-Member is slow. Don't invoke it on every property. Collect all found properties in a hashtable and pass it once to Add-Member or, since you create an empty object, directly to New-Object. To enforce the order of properties use Select-Object in PowerShell 2. Note, PowerShell 3.0 and newer support [ordered] and [PSCustomObject] typecast (see the code below).
Use pipelining instead of foreach statements so that the results appear immediately
Files are already sorted by name, at least on NTFS file system in Windows, so no need to sort.
Function Get-FileMetaData(
[string[]]$folders,
[string[]]$properties
) {
$shellApp = New-Object -ComObject Shell.Application
# get all headers and find their indexes
$shellFolder = $shellApp.namespace($folders[0])
$allProps = #{}
foreach ($index in 0..266) {
$allProps[$shellFolder.getDetailsOf($shellFolder.items, $index)] = $index
}
$supportsOrdered = $PSVersionTable.PSVersion.Major -ge 3
$hash = if ($supportsOrdered) { [ordered]#{} } else { #{} }
# walk the folders and get the properties by index found above
$folders | ForEach {
$shellFolder = $shellApp.namespace($_)
$shellFolder.items() | ForEach {
$file = $_
$hash.Clear()
foreach ($prop in $properties) {
if (($index = $allProps[$prop]) -ne $null) {
$hash[$prop] = $shellFolder.getDetailsOf($file, $index)
}
}
if ($supportsOrdered) {
[PSCustomObject]$hash
} else {
Select $properties -inputObject (
New-Object PSObject -Property $hash
)
}
}
}
}
Usage example 1:
Get-FileMetaData -folders 'r:\folder1', 'r:\folder2' -properties Name, Path, Comments
Usage example 2:
Get-FileMetaData -folders 'r:\folder1', 'r:\folder2' -properties Name, Path, Comments |
Export-Csv r:\results.csv -encoding UTF8 -NoTypeInformation
Usage example 3 gets all properties, which is slow:
Get-FileMetaData -folders 'r:\folder1', 'r:\folder2'

Download an image from website

I ran this powershell script to download an image from a website (to download it, certain steps had to be made, that's why I used IE navigate). I put a random string with a space between 4 and 4 characters.
But I get an error and it doesn't even start to fill the blank with the string:
Exception from HRESULT: 0x800A01B6
At E:\getbd.ps1:13 char:1
+ $ie.Document.getElementsByTagName("text") | where { $.name -eq "words ...
Here is the code:
$url = "https://fakecaptcha.com"
$set = "abcdefghijklmnopqrstuvwxyz0123456789".ToCharArray()
for($i=1; $i -le 4; $i++){
$result += $set | Get-Random}
$result += ' '
for($i=1; $i -le 4; $i++){
$result += $set | Get-Random}
$ie = New-Object -comobject InternetExplorer.Application
$ie.visible = $true
$ie.silent = $true
$ie.Navigate( $url )
while( $ie.busy){Start-Sleep 1}
$ie.Document.getElementsByTagName("text") | where { $.name -eq "words" }.value = $result
$generateBtn = $ie.Document.getElementsById('input') | Where-Object {$_.Type -eq 'submit' -and $_.Value -eq 'Create it now!'}
$generateBtn.click()
while( $ie.busy){Start-Sleep 1}
$readyBtn = $ie.Document.getElementsById('input') | Where-Object {$_.Type -eq 'button' -and $_.Value -eq 'Your captcha is done! Please click here to view it!!'}
$readyBtn.click()
while( $wc.busy){Start-Sleep 1}
$downloadBtn = $ie.Document.getElementsById('input') | Where-Object {$_.Type -eq 'button' -and $_.Value -eq 'DOWNLOAD'}
$downloadBtn.click()
while( $ie.busy){Start-Sleep 1}
$source = $ie.document.getElementsByTagName('img') | Select-Object -ExpandProperty src
$file = '$E:\bdlic\'+$result+'.jpg'
$wc = New-Object System.Net.WebClient
$wc.DownloadFile($source,$file)
while( $wc.busy){Start-Sleep 1}
$ie.quit()
You have 2 syntax errors in that line:
$ie.Document.getElementsByTagName("text") | where { $.name -eq "words" }.value = $result
# ^ ^^^^^^
$.Name: The "current object" variable is $_, not just $.
where {...}.value: You cannot use dot-notation on the scriptblock of a Where-Object statement. You need to put the entire statement in a (sub)expression for that.
Change the line to this:
($ie.Document.getElementsByTagName("text") | where { $_.name -eq "words" }).value = $result

Open D drive using powershell

I am editing a powershell script (2.0 I believe) that has the following code to open the cd drive:
$sh = New-Object -ComObject "Shell.Application"
$sh.Namespace(17).Items() |
Where-Object { $_.Type -eq "CD Drive" } |
foreach { $_.InvokeVerb("Eject") }
The problem is that this script now runs on a computer with multiple optical drives so they all open - I only want the D drive to open. I thought changing the script to the following would work:
$sh = New-Object -ComObject "Shell.Application"
$sh.Namespace(17).Items() |
Where-Object { $_.DriveID -eq "D:" } |
foreach { $_.InvokeVerb("Eject") }
but it did not (none of the drives opened). How can I only open the D drive?
The issue you're having is that the result of $sh.Namespace(17).Items() - at least for me - doesn't include a property called DriveID. It does include "Path" though. Try the below.
$sh = New-Object -ComObject "Shell.Application"
$sh.Namespace(17).Items() | ? {$_.Path -eq "D:\"} | % { $_.InvokeVerb("Eject") }
Try filtering on the Path property.
$sh = New-Object -ComObject "Shell.Application"
$sh.Namespace(17).Items() | Where-Object { $_.Path -eq "D:\" } | ForEach-Object { $_.InvokeVerb("Eject") }
I don't see a 'DriveID' property, so the Where-Object is filtering all objects (i.e. nothing is passed to the ForEach). Try using the 'Path' property instead?
$sh = New-Object -ComObject "Shell.Application"
$sh.Namespace(17).Items() |
Where-Object { $_.Path -eq "D:\" } |
foreach { $_.InvokeVerb("Eject") }

Extracting Content from Webpage with ParsedHtml

I've been trying to use the invoke-Webrequest and the "ParsedHtml.getElements"
ParsedHtml.getElementsByTagName("div") | Where{ $_.className -eq 'pricingContainer-priceContainer' } ).innerText
to try to get the value $8.29 but using it on the below code produces no result. What am I doing wrong?
<div class="pricingContainer pricingContainer--grid u-ngFade noCenterTag" ng-class="::{'noCenterTag': !showCenterTag}" ng-if="::featuresEnabled">
<!-- ngIf: ::(product.IsOnSpecial && !product.HideWasSavedPrice) -->
<div class="pricingContainer-priceContainer">
<span class="pricingContainer-priceAmount" ng-class="::specialClass">$8.29</span>
<!-- ngIf: ::product.CupPrice --><span ng-if="::product.CupPrice" class="pricingContainer-priceCup">
$5.19 / 100G
</span><!-- end ngIf: ::product.CupPrice -->
</div>
</div>
By replacing className by class:
($html.getElementsByTagName("span") | Where{ $_.class -eq 'pricingContainer-priceCup' }).innerText
or
($html.getElementsByTagName("div") | Where{ $_.class -eq 'pricingContainer-priceContainer' }).innerText
An example:
$Site = "http://example.com/index.html"
$all = Invoke-WebRequest -URI $Site
# $all contains all informations of the page
$html = [xml]$all.Content
#[xml] is a cast to convert code to xml
$html.getElementsByTagName("div")
You can use automation with IE. You choose a div witch contains the Card and you can get the innerHTML like this:
$ie = New-Object -ComObject "InternetExplorer.Application"
$ie.Navigate("http://www.example.com/index.html")
$ie.Visible = $true
while ($ie.Busy -eq $true) { Start-Sleep -Milliseconds 2000; }
$html= $ie.Document.body.getElementsByTagName('div') | Where-Object {$_.className -eq "cardList-cards cardList-isotopeContainer"}
$lines = $html.innerHTML.split("`n")
$prices = $lines | Where-Object { $_ -Match '<span class=\"pricingContainer\-priceAmount\"' }
$prices = $prices | foreach { [regex]::matches($_, '>([0-9.$]*)</span>').Groups[1].Value }
echo $prices
Worked this bad boy out by opening the webpage , wait for correct html to load over the dynamic html then dumps to a txt file to read and search.
$path = "c:\sourcecode.txt"
$ie = New-Object -com InternetExplorer.Application
$ie.visible=$true
$ie.navigate("blahblahblahblah insert webpage here")
while($ie.ReadyState -ne 4) {start-sleep -s 10}
$ie.Document.body.outerHTML | Out-File -FilePath $path
$pricebf = select-string -path $path -pattern "pricingContainer-priceAmount" | select-object -First 1 | select Line
$Descriptionbf = select-string -path $path -pattern "canOpenDetail --><a title=" | select-object -First 1 | select Line

Shell.Application - write new Values?

I have a directory with MP3 files. I can read the properties with this script. How I can write properties (Album, Genre, etc.)?
$com = (New-Object -ComObject Shell.Application).NameSpace('C:\Users\Peter\Music')
for( $index = 0; ((-not $bitrateAttribute) -or (-not $albumAttribute)); ++$index ) {
$name = $com.GetDetailsOf($com.Items,$index)
if ($name -eq 'Album') {$albumAttribute = $index}
if ($name -eq 'Bit rate') {$bitrateAttribute = $index}
}
$com.Items() | ForEach-Object {
New-Object -TypeName PSCustomObject -Property #{
Name = $_.Name
Album = $com.GetDetailsOf($_,$albumAttribute)
BitRate = $com.GetDetailsOf($_,$bitrateAttribute)
} | Select-Object -Property Name,Album,BitRate
}
Or is there a better way to write ID3 tags to MP3 files?