Get target of shortcut (.lnk) file with powershell - powershell

I have a bunch of .lnk files and need to treat them differently depending on the target that the shortcut points to. I've found very little of how to this with other languages, but nothing about doing this with powershell.
I've tried this:
$sh = New-Object -COM WScript.Shell
$target = $sh.CreateShortcut('<path>').Target
But this returns an empty string even though I can see in the .lnk properties that the Target is specified.
Any idea on how to accomplish this?

You have made an error in the property; as wOxxOm suggests, you should be using TargetPath rather than Target:
$sh = New-Object -ComObject WScript.Shell
$target = $sh.CreateShortcut('<full-path-to-shortcut>').TargetPath
Google and MSDN were indeed helpful here; additionally, piping objects to Get-Member can often be useful and educational. This question also shows how to manipulate shortcuts using PowerShell, and uses the same technique as seen here.
If you want the arguments to the executable as well, those are stored separately:
$arguments = $sh.CreateShortcut('<full-path-to-shortcut>').Arguments
Again, piping objects to Get-Member - in this case, the object returned by WScript.Shell.CreateShortcut() - provides useful information.
It should be noted that there are issues with using this technique and these calls when the path contains Unicode emoji characters; there is a workaround for this case in this StackOverflow question.

It may seem obvious to experts but to us simpletons there seems to be a key lightbulb moment here:
<full-path-to-shortcut> = the Full Name! Doh!
Make sure you use .FullName if you use Get_ChildItem | ForEach-Object, etc. with the Shell .CreateShortcut call if you want the current target of a shortcut. For me:
.CreateShortcut($_.FullName) returned an appropriate value; whereas
.CreateShortcut($_) returned 'null'

Related

Change Default Windows Sound With Powershell

I would like to find a way to change the windows default sound with powershell.
In vbs it is written like this:
WshShell.RegWrite "HKCU\AppEvents\Schemes\Apps\.Default\.Default\.Current\","c:\windows\media\horn.wav","REG_SZ"
I tried invoking the command but did not know the correct way to do it.
There are (at at least) two pretty simple ways to do this in PowerShell. The first is to use the WShell from within PowerShell (at least in Windows PowerShell 5.1 - unsure about Core):
$wshell = New-Object -ComObject WScript.Shell
$wshell.RegWrite("HKCU\AppEvents\Schemes\Apps\.Default\.Default\.Current\","c:\windows\media\horn.wav","REG_SZ")
The second is a more built-in way using PowerShell's Set-ItemProperty cmdlet - which should work in Core versions.
$SetItemProperty = #{
Path = "HKCU:\AppEvents\Schemes\Apps\.Default\.Default\.Current\"
Name = "(default)"
Value = "c:\windows\media\horn.wav"
}
Set-ItemPoperty #SetItemProperty
(Note: using the hashtable variable with the # symbol instead of the common $ symbol is called Splatting; it's effectively just matching up parameter names to values so I don't have to write out a really (horizontally) long command.)
Powershell does not have a native cmdlets for that, beside you would have to use C# to get that functionality in powershell.
Luckily, someone did the hardwork and put that in to a module, check the following repo :
https://github.com/frgnca/AudioDeviceCmdlets
There are detailed instructions on how to install the module, once you done that you can see all devices
Get-AudioDevice -List
Index : 1
Default : True
Type : Playback
Name : Speakers (2- Jabra EVOLVE LINK)
ID : {0.0.0.00000000}.{8c58263c-e6a0-4c7b-8e51-5231f04cbcb9}
Device : CoreAudioApi.MMDevice
From there you can see the current Default device and change it however you like.

What is the purpose of putting Class after your variable like this ($_.Class)?

What is the purpose of this string? I am still new to PowerShell and have seen this come up quiet a bit.
$_.Class
Here is an example of how I have seen it used:
$R.AllElements|?{$_.Class -eq "table"}|select innerText
Does this meant that we are applying a class on the $R variable? Or is there a "Class" already for that variable that we are searching for the string "table"? I have looked around but cannot find any explanation.
Here's an example for you to understand the purpose of $_.xxxxx
$Obj = Get-Item -Path 'C:\Program Files'
This would put any information gathered by the Get-Item command and store it in $Obj. If you then show what's in the variable, it will look something like this
If you would then do $obj.xxx where xxx is one of the property names shown above, it will show you the values under that property like this
Another way to get the values of said properties is how you have shown in your post. If you pipe the variable into a where statement like below, it is especially helpful when trying to filter out some text inside a particular variable. This example is looking for the text prog in the Name property of all the items inside of $Obj2
I wish I had time to give more details, I hope this helps you some! PowerShell is one of the best tools for Automation and Administration in a Windows environment so keep on learnin!
Bonus: Here is a link to documentation on PowerShell's pipelines and how it works, I referred to it as 'pipe' in my wordage above

How to get the target path to a lnk-file with powershell [duplicate]

I have a bunch of .lnk files and need to treat them differently depending on the target that the shortcut points to. I've found very little of how to this with other languages, but nothing about doing this with powershell.
I've tried this:
$sh = New-Object -COM WScript.Shell
$target = $sh.CreateShortcut('<path>').Target
But this returns an empty string even though I can see in the .lnk properties that the Target is specified.
Any idea on how to accomplish this?
You have made an error in the property; as wOxxOm suggests, you should be using TargetPath rather than Target:
$sh = New-Object -ComObject WScript.Shell
$target = $sh.CreateShortcut('<full-path-to-shortcut>').TargetPath
Google and MSDN were indeed helpful here; additionally, piping objects to Get-Member can often be useful and educational. This question also shows how to manipulate shortcuts using PowerShell, and uses the same technique as seen here.
If you want the arguments to the executable as well, those are stored separately:
$arguments = $sh.CreateShortcut('<full-path-to-shortcut>').Arguments
Again, piping objects to Get-Member - in this case, the object returned by WScript.Shell.CreateShortcut() - provides useful information.
It should be noted that there are issues with using this technique and these calls when the path contains Unicode emoji characters; there is a workaround for this case in this StackOverflow question.
It may seem obvious to experts but to us simpletons there seems to be a key lightbulb moment here:
<full-path-to-shortcut> = the Full Name! Doh!
Make sure you use .FullName if you use Get_ChildItem | ForEach-Object, etc. with the Shell .CreateShortcut call if you want the current target of a shortcut. For me:
.CreateShortcut($_.FullName) returned an appropriate value; whereas
.CreateShortcut($_) returned 'null'

Generating printer shortcuts from list

I am trying to generate a shortcut for every printer I have on a print server. The idea is to be able to email these shortcuts to people and when they click on them, it automatically installs that printer for them.
I've populated an array from a list of printer names exported from the print server:
$list = #((get-contnet $home\dekstop\plist.txt))
I then created a method to create a shortcut:
function Make-Shortcut
{
param ([string]$dest, [string]$source)
$WshShell = New-Object -comObject Wscript.Shell
$Shortcut = $WshShell.CreateShortcut($dest)
$Shortcut.TargetPath = $Source
$Shortcut.Save()
}
The function works fine. I was able to create standard shortcuts with no problem.
This next part is where I am getting stuck:
foreach ($i in $list)
{
Make-Shortcut "C:\pshort\$i.lnk" "C:\Windows\System32\rundll32.exe
printui.dll,PrintUIEntry /in /q /n\\printserver\$i"
}
When this runs, it does generate a shortcut with the same name as the printer for each printer on the list. However, the problem comes in at the target path. Instead of
C:\Windows\System32\rundll32.exe printui.dll,PrintUIEntry /in /q /n\\printserver\printername
it changes it to:
C:\Windows\System32\rundll32.exe printui.dll,PrintUIEntry \in \q \n\printserver\printername
The three problems with this are:
It is reversing the forward slash for the parameters
It is removing one of the backslashes preceding the server name
It is adding quotes to both sides. I need the quotes to come off for the shortcut to work properly.
I assume this is happening because Powershell thinks I am trying to make a standard shortcut and thinks I made mistakes while typing out the path.
I have tried putting a ` in front of each forward slash hoping the escape character would prevent it from reversing it, but no luck. I also tried using a hyphen for each parameter but that did not work either.
Is there anyway to stop this from happening? Or is there perhaps a better way to try to accomplish what I am trying to do?
You need to add arguments to the com object
Try adding a new param $arguments to your Make-Shortcut function and do:
Make-Shortcut "C:\pshort\$i.lnk" "C:\Windows\System32\rundll32.exe"
"printui.dll,PrintUIEntry /in /q /n\\printserver\$i"
add this in your function:
$Shortcut.Arguments = $arguments
So the link is created successfully ... but I have no idea if it works :)
Completely different answer but in a standard windows environment simply clicking a hyperlink to \printserver\printer will add a shared printer to someone's system?
So an email that simply lists :
\\PrintServer\Printer01
\\PrintServer\Printer02
\\PrintServer\Printer03
Would probably do the job just as well.

Question about $profile.psextended

I found a powershell tip regarding $profile.psextended that returns all of the various Powershell profiles and their locations. Having never see that before I ran ($profile | get-member). psextended was not in the list of results.
Having access to some very helpful MVPs on another site I asked how the psextended property was found. Within minutes I got a reply that using ($profile | gm -View all) should do the trick. I tried this and still had no results matching what I was looking for. If you enter $profile. and keep hitting the tab button it scrolls through the possible matches. psextended is nowhere to be found. Am I losing my mind or just plain blind?
Any help would be appreciated as I do not want to frustrate or annoy the guy trying to help me.
Thanks.
--
Eric
Every object, once assigned to a variable, is wrapped in a special semi-transparent object called a PSObject. This is powershell's way of trying to level the playing field for the different types of things that be assigned to a variable. There are four "special" properties exposing different things for any given variable in powershell:
ps> $o = 1
ps> $o.psobject
...
ps> $o.psadapted
...
ps> $o.psextended
...
ps> $o.psbase
...
You can read about these properties over on:
Link
Sorry. This was answered by Shay Levy (http://blogs.microsoft.co.il/blogs/ScriptFanatic/) on another service. Posted here to close the topic.
$profile | get-member -Force
lists the result I was looking for and several more for me to check out.