How can I use powershell to call SHGetKnownFolderPath? - powershell

I'm a total noob on windows powershell.
How can I use psl to call SHGetKnownFolderPath ?
I want to then also call SHSetKnownFolderPath if I dont like some of the values back from Get call.

You can use P/Invoke. Lee Holmes has an example of how to do this from PowerShell here. There's an example of how to use SHGetKnownFolderPoath here.
Alternatively, you might just be able to use Environment.GetFolderPath:
PS> [Environment]::GetFolderPath('CommonApplicationData')
C:\ProgramData
You can get the list of available options by the following:
PS> [Enum]::GetNames([Environment+SpecialFolder])

Before you delve into static methods in the Framework, look at the variables in the Env: PSDrive.
get-childitem env:
(get-item env:\CommonProgramFiles).Value

According to this Tutorial (and others) the settings are in the registry:
[HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Explorer\Shell Folders]
[HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Explorer\User Shell Folders]
Those are an easy read in powershell, access them as a drive:
ps> cd hkcu:\Software\Microsoft\Windows\CurrentVersion\Explorer\
ps> dir
And use the standard powershell tools to read these settings.
NOTE: You can set them with powershell here however that might ruin your day.
If you use the explorer to change the directory, it also moves the directory and stores the settings on multiple locations, like both 'User Shell Folders' and 'Shell Folders'.

Here's a function you can use that will use SHGetKnownFolderPath to convert a well-known folder guid to its current path:
Function GetKnownFolder([string]$KnownFolderCLSID) {
$KnownFolderCLSID = $KnownFolderCLSID.Replace("{", "").Replace("}", "")
$GetSignature = #'
[DllImport("shell32.dll", CharSet = CharSet.Unicode)]public extern static int SHGetKnownFolderPath(
ref Guid folderId,
uint flags,
IntPtr token,
out IntPtr pszProfilePath);
'#
$GetType = Add-Type -MemberDefinition $GetSignature -Name 'GetKnownFolders' -Namespace 'SHGetKnownFolderPath' -Using "System.Text" -PassThru -ErrorAction SilentlyContinue
$ptr = [intptr]::Zero
[void]$GetType::SHGetKnownFolderPath([Ref]"$KnownFolderCLSID", 0, 0, [ref]$ptr)
$result = [System.Runtime.InteropServices.Marshal]::PtrToStringUni($ptr)
[System.Runtime.InteropServices.Marshal]::FreeCoTaskMem($ptr)
return $result
}
Usage example:
GetKnownFolder "C4AA340D-F20F-4863-AFEF-F87EF2E6BA25"
Will return
C:\Users\Public\Desktop
Reference for Well-Known folder GUID's can be found at Microsoft

Related

PowerShell Get process by its handle

$ActiveHandle = [UserWindows]::GetForegroundWindow()
$Process = Get-Process | ? {$_.MainWindowHandle -eq $ActiveHandle}
This code retrieves a title of the current active window. Problem is that it only filters processes by MainWindowHandle. For example, if my active handle is a popup from the same process, it doesn't return anything as the handle is not its main handle. How can I modify the code to check for ALL handles instead of just the main one? Or rather, how can I retrieve all process handles?
I do not want to use external tools like WASP.
You can use the GetWindowThreadProcessId Win32 API function for this:
# Define a type that allows us to call the relevant win32 api
$user32 = Add-Type -MemberDefinition #'
[DllImport("user32.dll", SetLastError=true)]
public static extern uint GetWindowThreadProcessId(IntPtr hWnd, out uint lpdwProcessId);
'# -PassThru -Name user32Func
# prepare a variable to receive the target process id
$procId = 0
# call the function with, pass the handle as the first argument
$threadId = $user32::GetWindowThreadProcessId($ActiveHandle, [ref]$procId)
# If the owning thread and process was identified, the return-value will have been non-0
if($threadId) {
Write-Host "Found process $(Get-Process -Id $procId |% Name) with PID $procId"
}
else {
Write-Host "No owning process found"
}

Call Write-Verbose from C#

I load an "inline-type" in PowerShell like so:
$source = " some c# here "
$type = Add-Type -TypeDefinition $source -PassThru
$myvar = [MyClass]::new()
$myvar.Do()
How to call Write-Verbose from within the C# snippet, i.e., from the source in $source?
I only find examples on the Internet which perform WriteVerbose calls if the C# code contains a Cmdlet - which it does not in my case. Also I can find code how to call cmdlets in general, however, I don't want to create a new runspace, is there maybe any way to get the current runspace?
I found a solution, it is possible to pass a delegate to the C# code like so:
$writeVerboseDelegate = [Action[string]]{param($s) Write-Verbose $s; }
...
$myvar = [MyClass]::new($writeVerboseDelegate)

PowerShell - automate comment header

When writing up a PowerShell script, what options are there to support template insertion that infers your parameters?
e.g. for the below, it'd somehow read you have $Name as a parameter, and then automatically produce something like below?
.SYNOPSIS
.DESCRIPTION
.PARAMETER Name
.PARAMETER Extension
function Add-Extension
{
param ([string]$Name,[string]$Extension = "txt")
$name = $name + "." + $extension
$name
<#
.SYNOPSIS
Adds a file name extension to a supplied name.
.DESCRIPTION
Adds a file name extension to a supplied name.
Takes any strings for the file name or extension.
.PARAMETER Name
Specifies the file name.
.PARAMETER Extension
Specifies the extension. "Txt" is the default.
.INPUTS
None. You cannot pipe objects to Add-Extension.
.OUTPUTS
System.String. Add-Extension returns a string with the extension or file name.
.EXAMPLE
C:\PS> extension -name "File"
File.txt
.EXAMPLE
C:\PS> extension -name "File" -extension "doc"
File.doc
.EXAMPLE
C:\PS> extension "File" "doc"
File.doc
.LINK
Online version: http://www.fabrikam.com/extension.html
.LINK
Set-Item
#>
}
What you're looking for is a feature of a PowerShell-aware editor or IDE.
The actively developed editor that offers the best PowerShell development experience, across platforms, is Visual Studio Code, combined with its PowerShell extension.
Once you have authored a function's implementation, you can place the cursor at either the start or the end of the function body and type ##, which scaffolds comment-based help for the function based on its parameters.
See also: Get-Help about_Comment_Based_Help, which explains the syntax of comment-based help.
If you want to discover the parameters of a defined function, you can do something like this:
# define function
function Add-Extension {
param([string]$Name, [string]$Extension)
}
# get command info for defined function
$cmdInfo = Get-Command Add-Extension
# compile command metadata from command info
$cmdMetadata = [System.Management.Automation.CommandMetadata]::new($cmdInfo)
Now you can access the individual parameters' metadata via $cmdMetadata.Parameters and generate your comment-based help content:
foreach($param in $cmdMetadata.Parameters.GetEnumerator()) {
".PARAMETER {0}" -f $param.Key
"[Insert parameter description for {0}]" -f $param.Key
""
}
There's one thing you might be interesting in that the parameter metadata won't contain though - the default value expression - for that, you'll need to parse the raw source code instead:
# parse script file containing the function
$scriptPath = Resolve-Path .\scriptWithFunctionDefinition.ps1
$AST = [System.Management.Automation.Language.Parser]::ParseFile($scriptPath, [ref]$null, [ref]$null)
# discover all the function definitions at the root level
$functionDefs = $AST.FindAll({ $args[0] -is [System.Management.Automation.Language.FunctionDefinitionAst] }, $false)
The Parser.ParseFile() method will return an abstract syntax tree - or AST for short - the elements of which we can inspect.
The FindAll() method will walk the AST and output any subtree that satisfies the condition - in this case we're looking for any [FunctionDefinitionAst], which, as the name implies, describes a function definition.
Now we can iterate the parameters like before:
foreach($functionDefinition in $functionDefs) {
# Since we're effectively dealing with code still,
# we need to handle both `function f(){}` and `function f{param()}` syntaxes
$parameters = $functionDefinition.Parameters
if($functionDefinition.Body.ParamBlock){
$parameters = $functionDefinition.Body.ParamBlock
}
# Now we can go through the actual parameters
foreach($parameter in $parameters){
".PARAMETER {0}" -f $parameter.Name.VariablePath
# ... and extract the default value expression
if($parameter.DefaultValue){
"Default value is {0}" -f $parameter.DefaultValue
}
}
}

Mapping of process name and application name using powershell

I need to track foreground application with application name in window machine. I am using given code but it provide ProcessName not application name example ProcessName is "chrome" Apliication name is "Google Chrome". Either I get application name direclty or i able to mapped Application name with process name.Please help me into this
[CmdletBinding()]
Param(
)
Add-Type #"
using System;
using System.Runtime.InteropServices;
public class UserWindows {
[DllImport("user32.dll")]
public static extern IntPtr GetForegroundWindow();
}
"#
try{
$ActiveHandle = [UserWindows]::GetForegroundWindow()
$Process = Get-Process | ? {$_.MainWindowHandle -eq $activeHandle}
$Process | Select ProcessName, #{Name="AppTitle";Expression= {($_.MainWindowTitle)}}
}catch{
Write-Error "Failed to get active Window details. More info:$_"
}
Rather than give you the answer (since you may have more questions after) I will teach to to fish for yourself.
Lets say you have a variable and you want to see all of its properties you can get from it; run $variable |get-member
Now you see there are a lot of properties attached to your variable and you don't see any called "application name". So lets list all of the properties of this variable and see what gives us the value we are looking for.
For my example I will grab chrome to put into my variable so we are on the same page.
Here is the code I used to grab the variable to match what you would be working with (ignore this if you already have your variable you're working with).
$variable= Get-Process|? name -ilike chrome|select -first 1
Lets list all the properties
$variable|format-list *
Now we see there are 2 properties that list the name were looking for, Description and Product (for chrome either works, but i don't know which will work for your other use cases, possibly none). Lets grab Product and use that for your code, substituting processname in the select statement (the statement that selects what properties to keep/show in that variable) with the Product Property... Now that you know how, you can change if need be =)
[CmdletBinding()]
Param(
)
Add-Type #"
using System;
using System.Runtime.InteropServices;
public class UserWindows {
[DllImport("user32.dll")]
public static extern IntPtr GetForegroundWindow();
}
"#
try{
$ActiveHandle = [UserWindows]::GetForegroundWindow()
$Process = Get-Process | ? {$_.MainWindowHandle -eq $activeHandle}
$Process | Select Product, #{Name="AppTitle";Expression= {($_.MainWindowTitle)}}
}catch{
Write-Error "Failed to get active Window details. More info:$_"
}

Get foreground windows of windows explorer to retrieve the url

I think am close to the solution, or in the general ball park, but the way am calling the powershell might be part of the issue am calling the ps1 from a batch file, to
I want to have the user click a button and then have powershell get the url of the active window (EXPLORER.exe) - i did try to but the start/sleep command to check in the ISE but could get it to work :( could you help be out ?
Thank You
Set-ExecutionPolicy RemoteSigned
Param(
)
Add-Type #"
using System;
using System.Runtime.InteropServices;
public class UserWindows {
[DllImport("user32.dll")]
public static extern IntPtr GetForegroundWindow();
}
"#
try {
$ActiveHandle = [UserWindows]::GetForegroundWindow()
$Process | Select LocationURL| select -ExpandProperty LocationURL -First 1 |
Tee-Object -Variable "dirvar"
} catch {
Write-Error "Failed to get active Window details. More Info: $_"
}
$dirvar1 = "$dirvar" -replace 'file:///', ''
echo "$dirvar1"
Start-Process "Z:\30_Sysadmin\ADB_LOADER_DIR\ADBDIR.bat" "$dirvar1"
You are going about it in a difficult way. There are helper functions. This lists all shell windows (Internet Explorer and Explorer windows). The object is an Internet Explorer object. See https://msdn.microsoft.com/en-us/library/aa752084(v=vs.85).aspx.
This is VBScript but COM calls are identical across all languages.
Set objShell = CreateObject("Shell.Application")
Set AllWindows = objShell.Windows
For Each window in AllWindows
window.Navigate "c:\"
msgbox window.locationURL
Next