Create Shortcut using PowerShell Script - powershell

I am trying to create a shortcut on desktop using a Powershell Script. However, I got an error code when trying to run the code below.
$new_object = New-Object -ComObject WScript.Shell
$destination = $new_object.SpecialFolders.Item("AllUsersDesktop")
$source_path = Join-Path -Path $destination -ChildPath "\\Test Intranet.url"
$source = $new_object.CreateShortcut($source_path)
$source.TargetPath = "https://sharepoint.com/"
$source.IconLocation="C:\Users\Public\Pictures\ShortcutIcon.ico"
$source.Save()
Any help will be appreciated.
Thanks.

You didn't show this, but the error message you received is probably this one:
Exception setting "IconLocation": "The property 'IconLocation' cannot be found on this object. Verify that the property exists and can be set."
At line:8 char:1
+ $source.IconLocation="C:\Users\Public\Pictures\ShortcutIcon.ico"
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : NotSpecified: (:) [], SetValueInvocationException
+ FullyQualifiedErrorId : ExceptionWhenSetting
That is because an Internet shortcut has different properties than a 'normal' (.lnk) shortcut to a file of folder.
Another thing is that you have prefixed the shortcut filename with a double backslash and by doing so, you will get a wrong path: C:\Users\Public\Desktop\\Test Intranet.url
In below code, I have changed some of the variable names to be more self-descripting (at least, I like to think so..)
$shell = New-Object -ComObject WScript.Shell
$destination = $shell.SpecialFolders.Item("AllUsersDesktop")
$shortcutPath = Join-Path -Path $destination -ChildPath 'Test Intranet.url'
# create the shortcut
$shortcut = $shell.CreateShortcut($shortcutPath)
# for a .url shortcut only set the TargetPath
$shortcut.TargetPath = 'https://sharepoint.com/'
$shortcut.Save()
# next update the shortcut with a path to the icon file and the index of that icon
# you can do that because a .url file is just a text file in INI format
Add-Content -Path $shortcutPath -Value "IconFile=C:\Users\Public\Pictures\ShortcutIcon.ico"
Add-Content -Path $shortcutPath -Value "IconIndex=0"
# clean up the COM objects
[System.Runtime.Interopservices.Marshal]::ReleaseComObject($shortcut) | Out-Null
[System.Runtime.Interopservices.Marshal]::ReleaseComObject($shell) | Out-Null
[System.GC]::Collect()
[System.GC]::WaitForPendingFinalizers()
When opened in notepad, your shortcut file looks like this:
[{000214A0-0000-0000-C000-000000000046}]
Prop3=19,11
[InternetShortcut]
IDList=
URL=https://sharepoint.com/
IconFile=C:\Users\Public\Pictures\ShortcutIcon.ico
IconIndex=0

Related

Concatenate filename with current directory path

I'm trying to execute a PowerShell script that opens an Excel file and does a SaveAs with it. the problem is that it works when I put the full path for the file, but I want it to be directed to the working folder, that way if I take the script and files to other location I don't have to modify the script for each environment.
$excel = New-Object -ComObject Excel.Application
$excel.Visible = $true
$excel.DisplayAlerts = $false
$wb = $excel.Workbooks.Open("E:\TEMP\TEMPLATE4WEEKS.xlsx")#Opening like this works
$wb = $excel.Workbooks.Open(ThisWorkbook.Path & "\TEMPLATE4WEEKS.xlsx") #this is not working
$wb.SaveAs(ThisWorkbook.Path & "\TEMPLATE4WEEKSprotected.xlsx",[Type]::Missing,"password") #this is not working
$excel.Quit()
I was trying to concatenate current folder so it will work if I say move the entire folder to another path.
Right now the folder that contains the PS1 script is the same that contains the TEMPLATE4WEEKS.xlsx file. I want all to operate inside the same folder.
Changed as per Ansgar's response:
$excel = New-Object -ComObject Excel.Application
$excel.Visible = $true
$excel.DisplayAlerts = $false
$file = Join-Path $excel.ThisWorkbook.Path "TEMPLATE4WEEKS.xlsx"
$wb = $excel.Workbooks.Open($file)
$wb.SaveAs($file,[Type]::Missing,"password")
$excel.Quit()
I'm getting this error now:
Join-Path : Cannot bind argument to parameter 'Path' because it is null.
At C:\Users\AGUIRRG2\Desktop\Newfolder\PasswordProtect4WEEKSCopy.ps1:4 char:19
+ $file = Join-Path $excel.ThisWorkbook.Path "TEMPLATE4WEEKS.xlsx"
+ ~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidData: (:) [Join-Path], ParameterBindingValidationException
+ FullyQualifiedErrorId : ParameterArgumentValidationErrorNullNotAllowed,Microsoft.PowerShell.Commands.JoinPathCommand
Sorry, we couldn't find . Is it possible it was moved, renamed or deleted?
At C:\Users\AGUIRRG2\Desktop\Newfolder\PasswordProtect4WEEKSCopy.ps1:5 char:1
+ $wb = $excel.Workbooks.Open($file)
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : OperationStopped: (:) [], COMException
+ FullyQualifiedErrorId : System.Runtime.InteropServices.COMException
You cannot call a method on a null-valued expression.
At C:\Users\AGUIRRG2\Desktop\Newfolder\PasswordProtect4WEEKSCopy.ps1:6 char:1
+ $wb.SaveAs($file,[Type]::Missing,"password")
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidOperation: (:) [], RuntimeException
+ FullyQualifiedErrorId : InvokeMethodOnNull
& in PowerShell is not a concatenation operator. Plus, you don't want to build paths via string concatenation in the first place. You also cannot use the object ThisWorkbook directly in PowerShell (that only works in VBA, and it's the location of the current macro, so it won't work here at all because you're not running a macro).
Change this:
$wb = $excel.Workbooks.Open(ThisWorkbook.Path & "\TEMPLATE4WEEKSxlsx")
into this:
$wb = $excel.Workbooks.Open("${PSScriptRoot}\TEMPLATE4WEEKS.xlsx")
Note: The automatic variable $PSScriptRoot was introduced with PowerShell v3 and doesn't exist in earlier versions. If you're running an older version you need to define it yourself, e.g. like this:
$PSScriptRoot = Split-Path $MyInvocation.MyCommand.Path -Parent
To use a relative path instead of a physical one, use the variable $pwd. It's your current working directory:
$excel = New-Object -ComObject Excel.Application
$excel.Visible = $true
$excel.DisplayAlerts = $false
$LoadPath = Join-Path -Path $pwd -ChildPath "TEMPLATE4WEEKS.xlsx" #$pwd is your current working dir
$SavePath = Join-Path -Path $pwd -ChildPath "TEMPLATE4WEEKSprotected.xlsx"
$wb = $excel.Workbooks.Open($LoadPath)
$wb.SaveAs($SavePath,[Type]::Missing,"password")
$excel.Quit()

Powershell ZIP CopyHere counteracting asynchronous behavior

Within Powershell, the CopyHere method for the Shell-Application Namespace is asynchronous. My main goal with this is to convert a KML file to a KMZ file. The process of doing this is to create a ZIP file with the same name, copy the KML into the KMZ (compresses the file) and then rename the ZIP to KMZ. Unfortunately, being asynchronous means the rename function is being called before the CopyHere method is completed. I have found many examples of solving this. The cleanest one I found is below:
$kmlPath = $global:directoryPath + "Test.kml"
$zip = $global:directoryPath + "Test.zip"
New-Item $zip -ItemType file
$shellApplication = new-object -com shell.application
$zipPackage = $shellApplication.NameSpace($zip)
$zipPackage.CopyHere($kmlPath, 16)
while($zipPackage.Items().Item($zip.Name) -Eq $null)
{
start-sleep -seconds 1
write-host "." -nonewline
}
write-host "."
Rename-Item -Path $zip -NewName $([System.IO.Path]::ChangeExtension($zip, ".kmz"))
This responds with the following error:
Exception calling "Item" with "1" argument(s): "Not implemented
(Exception from HRESULT: 0x80004001 (E_NOTIMPL))"
+ while($zipPackage.Items().Item($zip.Name) -Eq $null)
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : NotSpecified: (:) [], MethodInvocationException
+ FullyQualifiedErrorId : ComMethodTargetInvocation
Am I misusing the Item method for this particular package? I am confused why something that "appears" to be neatly done is not working. I have also tried the snippet of code provided Here. It also complains about the .Item method in this particular situation.
The issue i ran into was trying to find away to check on zip status.
So instead i did a trigger for a while that would fire ...If the Zipfile was openable and the File name was inside.
function kml_to_kmz([string]$kmlPath){
[Reflection.Assembly]::LoadWithPartialName('System.IO.Compression.FileSystem')
$kmlInfo = Get-ChildItem -Path $kmlPath
$zipLocation = ($kmlInfo.Directory.FullName + '\' + $kmlInfo.Name.Remove($kmlInfo.Name.LastIndexOf('.')) + '.zip')
New-item $zipLocation
((new-object -com shell.application).NameSpace($zipLocation)).CopyHere($kmlPath, 16)
$trigger = $false
while ($trigger -eq $false){
try{
$zip = [IO.Compression.ZipFile]::OpenRead($zipLocation)
If(($zip.Entries | %{$_.Name}) -contains $kmlInfo.Name){
$zip.Dispose();
$trigger = $true
break;
}
}catch{}
start-sleep -seconds 1
write-host "." -nonewline
}
[IO.Compression.ZipFile]::OpenRead($zipLocation).Dispose()
Rename-Item -Path $zipLocation -NewName $([System.IO.Path]::ChangeExtension($zipLocation, '.kmz'))
}
kml_to_kmz -kmlPath "C:\Users\Default\Desktop\Test.kml"

Loading MailKit DLL as Assembly in Powershell

I'm trying to use MailKit dll as assembly in Powershell but there is no way it works.
I've tried with add-type and with [System.Reflection.Assembly] methods bwithout success.
The link to mailkit library:
https://github.com/jstedfast/MailKit
With this method :
$path="$HOME\.nuget\packages\mailkit\1.16.1\lib\net451\MailKit.dll"
[System.Reflection.Assembly]::LoadFile($path)
ther isn't reference to the assembly in memory.
With this method :
Add-Type -Path $path
this is the error:
Add-Type -Path $path
~~~~~~~~~~~~~~~~~~~~
CategoryInfo : NotSpecified: (:) [Add-Type], ReflectionTypeLoadException
FullyQualifiedErrorId : System.Reflection.ReflectionTypeLoadException,Microsoft.PowerShell.Commands.AddTypeCommand
Thanks
Daniele
this full script may help others:
# search for "Test" in subject and MoveTo Archive/2018
$packages = split-path -parent $MyInvocation.MyCommand.Definition
add-type -path (Join-Path $packages "MimeKit.dll") | Out-Null
add-type -path (Join-Path $packages "MailKit.dll") | Out-Null
#Server and Mailbox Definitions
$mailserver = "mail.corp.com"
$username = "email#corp.com"
$password = "password"
$cnn = New-Object MailKit.Net.Imap.ImapClient
$cnn.Connect($mailserver)
$cnn.Authenticate($username,$password)
$cnn.Inbox.Open([MailKit.FolderAccess]::ReadWrite)
$query = [MailKit.Search.SearchQuery]::SubjectContains("Test")
#$orderBy = #([MailKit.Search.OrderBy]::Arrival)
#filter
$uids = $cnn.Inbox.Search($query) #$orderby) not working yet
#download
$msgs = $cnn.Inbox.Fetch($uids, [MailKit.MessageSummaryItems]::UniqueId -bor [Mailkit.MessageSummaryItems]::BodyStructure)
#do something
#move
$archive = $cnn.GetFolder("Archive.2018")
$cnn.Inbox.MoveTo($uids, $archive)
$cnn.Disconnect($true)
Check the path. For me works just fine with the absolute path in $MailKitDllPath:
Add-Type -Path $MailKitDllPath
$client = New-Object MailKit.Net.Smtp.SmtpClient
I've found that MailKit had a reference to MimeKit dll, but there is no error loading MailKit.dll, so it's necessary to load MimeKit.dll also.
[System.Reflection.Assembly]::LoadFile("$home\.nuget\packages\MailKit\1.16.1\lib\net451\MailKit.dll")
[System.Reflection.Assembly]::LoadFile("$home\.nuget\packages\mimekit\1.16.1\lib\net451\MimeKit.dll")

Current directory apparently not current directory

Running this code in ISE works.
Push-Location -Path $(Split-Path -Parent $myInvocation.MyCommand.Path)
Get-Location
$file = '.\ex.txt'
$reader = New-Object System.IO.StreamReader($file)
Running the same code in Console fails. What am I missing?
PS H:\src\powershell> .\ccount.ps1
Path
----
H:\src\powershell
New-Object : Exception calling ".ctor" with "1" argument(s): "Could not find file
'C:\src\powershell\ex.txt'."
At H:\src\powershell\ccount.ps1:9 char:11
+ $reader = New-Object System.IO.StreamReader($file)
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidOperation: (:) [New-Object], MethodInvocationException
+ FullyQualifiedErrorId : ConstructorInvokedThrowException,Microsoft.PowerShell.Commands
.NewObjectCommand
How this is different from the suggested duplicate
The other question/answer does give an explanation for why PowerShell fails in this case. However, it does not give any hint as to why this works in ISE. This seems to be a significant difference between the Console and ISE host.
I am running PSVersion 5.0.10586.117 on Windows 7 Enterprise SP1.
Push-Location -Path $(Split-Path -Parent $myInvocation.MyCommand.Path)
$myInvocation.MyCommand.Path
Get-Location
$file = Resolve-Path '.\ex.txt'
$reader = New-Object System.IO.StreamReader($file)
Just use:
Push-Location $PSScriptRoot
It will work in both cases as long as you're using a recent PowerShell version (v3+).
The answer appears to be that Push-Location does not change [Environment]::CurrentDirectory in the console host. It does change it in ISE.
PS 09:02 \\SRV1\SH1\home\pwatson2 H:\
>Push-Location H:\src\t
PS 09:02 \\SRV1\SH1\home\pwatson2 H:\src\t
>Get-Location
Path
----
H:\src\t
PS 09:02 \\SRV1\SH1\home\pwatson2 H:\src\t
>Write-Host ([Environment]::CurrentDirectory)
C:\Windows\System32\WindowsPowerShell\v1.0

powershell doesn't install AzurePowerShell

I found script on internet, which install WindowsAzurePowerShell, but it doesn't work:
[reflection.assembly]::LoadWithPartialName("Microsoft.Web.PlatformInstaller") | Out-Null
$ProductManager = New-Object Microsoft.Web.PlatformInstaller.ProductManager
$ProductManager.Load()
$product = $ProductManager.Products | Where { $_.ProductId -eq "WindowsAzurePowerShell" }
$InstallManager = New-Object Microsoft.Web.PlatformInstaller.InstallManager
$Language = $ProductManager.GetLanguage("en")
$installertouse = $product.GetInstaller($Language)
$installer = New-Object 'System.Collections.Generic.List[Microsoft.Web.PlatformInstaller.Installer]'
$installer.Add($installertouse)
$InstallManager.Load($installer)
$failureReason=$null
foreach ($installerContext in $InstallManager.InstallerContexts) {
$InstallManager.DownloadInstallerFile($installerContext, [ref]$failureReason)
}
$InstallManager.StartInstallation()
I see exception:
Exception calling "DownloadInstallerFile" with "2" argument(s): "The InstallerContext passed to this method requires a non-Null InstallerFile."
At C:\Users\test.ps1:18 char:5
+ $InstallManager.DownloadInstallerFile($installerContext, [ref]$failureReason ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : NotSpecified: (:) [], MethodInvocationException
+ FullyQualifiedErrorId : InvalidOperationException
So. How it's fixed?
This example I have put together below doesn't go about installing that package the way you where attempting but instead downloads the WebPi CLI, extracts the zip and runs it in an elevated prompt then returns an exit code. I think this will get the job done and can be reproduced on all boxes that may not have the WebPI installed as you can place the file on a share and run this across many computers.
$SourcePath = "http://www.iis.net/community/files/webpi/webpicmd_x86.zip"
$DestinationPath = "c:\Temp\webpicmd_x86.zip"
$ExtractionPath = "c:\Temp\WebPICmd"
$CWebPiCmdLineTool = "$ExtractionPath\WebpiCmdLine.exe"
Import-Module BitsTransfer
Start-BitsTransfer -Source $SourcePath -Destination $DestinationPath
New-Item -Path C:\Temp -Name WebPICmd -ItemType directory | Out-Null
$shell = new-object -com shell.application
$zip = $shell.NameSpace($DestinationPath)
foreach($item in $zip.items())
{
$shell.Namespace($ExtractionPath).copyhere($item)
}
$InstallWebPiPackages = Start-Process -FilePath $CWebPiCmdLineTool -ArgumentList "/Products:WindowsAzurePowerShell" -Verb "RunAs" -Wait -PassThru
$InstallWebPiPackages.ExitCode