I want to create a set of aliases in a file that I can update, then invoke an alias aa so that the file executes and I am provided with the new aliases in the current session. Ultimately I wanted these aliases available automatically on PS startup so I am using C:\Users\Administrator\Documents\WindowsPowerShell\Microsoft.PowerShell_profile.ps1 to put them in. Right now it looks like:
$psdir="C:\Users\Administrator\Documents\WindowsPowerShell"
function Reload-Profile{
# . ${profile} DOES NOT WORK
# Write-Host ${profile}
# Write-Host $profile
# powershell $profile DOES NOT WORK
# Get-ChildItem "${psdir}\*.ps1" | %{.$_} DOES NOT WORK
Write-Host "Custom PowerShell Environment Loaded"
}
function Edit-Profile
{
powershell_ise.exe $profile
}
Set-Alias show Get-ChildItem
Set-Alias show2 Get-ChildItem
Set-Alias aa Reload-Profile
Set-Alias ep Edit-Profile
How can I do this so that the aliases are loaded on startup, yet I can also update them with an aa alias and have them brought into the current session?
If the original author of the comment that contained the following code decides to post it as an answer, just add a comment to this answer and I will remove it. As it has been two days I don't really expect him to. Meanwhile this should give people a better idea of what is actually happening.
# https://msdn.microsoft.com/en-us/powershell/reference/5.1/microsoft.powershell.utility/add-type
# Create a new cmdlet Reload-Profile using C# code and import it
Add-Type '
using System.Management.Automation;
using System.Management.Automation.Runspaces;
// https://msdn.microsoft.com/en-us/library/dd878294(v=vs.85).aspx
[Cmdlet("Reload", "Profile")]
public class ReloadProfileCmdlet : PSCmdlet {
protected override void EndProcessing()
{
// https://msdn.microsoft.com/en-us/library/ms568378(v=vs.85).aspx
// Runs $profile without parameters in the current context and displays the output and error
InvokeCommand.InvokeScript(". $profile", false, PipelineResultTypes.Output | PipelineResultTypes.Error, null);
}
}' -PassThru | Select -First 1 -ExpandProperty Assembly | Import-Module;
# Setup an alias for the new cmdlet
Set-Alias aa Reload-Profile
For better readability/highlighting the C# code standalone:
using System.Management.Automation;
using System.Management.Automation.Runspaces;
// https://msdn.microsoft.com/en-us/library/dd878294(v=vs.85).aspx
[Cmdlet("Reload", "Profile")]
public class ReloadProfileCmdlet : PSCmdlet {
protected override void EndProcessing()
{
// https://msdn.microsoft.com/en-us/library/ms568378(v=vs.85).aspx
// Runs $profile without parameters in the current context and displays the output and error
InvokeCommand.InvokeScript(". $profile", false, PipelineResultTypes.Output | PipelineResultTypes.Error, null);
}
}
The problem with your code is, that Reload-Profile is a function and when you invoke it, it will create new scope for itself. When you then invoke . $profile, it will not create new scope for profile, but it still be invoked inside Reload-Profile scope. Thus, when Reload-Profile ends, the scope will be discarded. So, you need to invoke Reload-Profile with dot invoke operator as well: . Reload-Profile or . aa, if you use alias.
I assume, your real question is "How to make aa command in a way, which does not require to use dot invoke operator?"
The answer will be to use compiled cmdlet instead of PowerShell function, because PowerShell does not create new scope for cmdlet invocation. That cmdlet can then invoke . $profile in current scope.
Add-Type #‘
using System.Management.Automation;
using System.Management.Automation.Runspaces;
[Cmdlet("Reload", "Profile")]
public class ReloadProfileCmdlet : PSCmdlet {
protected override void EndProcessing() {
InvokeCommand.InvokeScript(
". $profile",
false,
PipelineResultTypes.Output | PipelineResultTypes.Error,
null
);
}
}
’# -PassThru | Select -First 1 -ExpandProperty Assembly | Import-Module
Set-Alias aa Reload-Profile
P.S.
I recommend you to use different verb instead of Reload, because Reload is not included in a list of recommended verbs, thus Import-Module will issue warning, when you import Reload-Profile into your session.
Related
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:$_"
}
Suppose I have scripts AAA.ps1 and script BBB.ps1. Assume they are in the same location (directory).
Script BBB.ps1 defines a function BFunc which I want to invoke from AAA.ps1
How do I modify AAA.ps1 so that it can define-and-use BFunc?
As a test I have tried the following two commands in a script.
& "$PSScriptRoot\BBB.ps1"
get-childitem function:* | Select-Object Name | where { $_ -match "BFunc" }
If the above worked then I should see some output that BFunc is defined but nothing comes out.
Ah, sometimes it is too simple. It turns out that what PS expects is that BBB.ps1 be included into AAA.ps1. This means that the AAA.ps1 file could look like this:
. "$PSScriptRoot\BBB.ps1"
get-childitem function:* | Select-Object Name | where { $_ -match "BFunc" }
And that works!
I am trying to test some custom DSC resources written as a class with the help of pester. However I am struggling to work out how to make the class available to a different file. This is not a pester issue really, I can not do this in powershell either. I have a module with a class in it much like the following in a SxDeployment.psm1 file
[DscResource()]
class SxWindowsService {
[void]Set(){
}
[bool]Test(){
return $true;
}
[SxWindowsService]Get(){
return $this
}
}
This module has a corresponding .psd1, which is listing the SxWindowsService class as a 'DscResourcesToExport'. It is being registered as a module, I can see this module when I do a Get-Module -ListAvailable and can also perform an Import-Module upon it.
I suppose my question really is how can I create a a reference to this class from another file?
Say I have a different file test.ps1 with the following
Import-Module SxDeployment
$class = [SxWindowsService]::new()
I get the following error, "Unable to find type [SxWindowsService]."
UPDATE
After a little more tinkering, I can create an instance of the class by changing the .psm1 file to a .ps file and changing the import-module statement for . .\SxDeployment.ps1. So it seems the problem is how do you consume resources defined in a DSC resource module file outside of a DSC configuration?
The type literal [SxWindowsService] won't be accessible outside the module. This is by design. Here is a quote from the release notes:
Class keyword >>> Defines a new class. This is a true .NET Framework type.
Class members are public, but only public within the module scope. You can't refer to the type name as a string (for example, New-Object doesn't work), and in this release, you can't use a type literal (for example, [MyClass]) outside the script/module file in which the class is defined.
Edit:
Well after having said the above. Since there is a bit of a mismatch between object activation in .NET and PowerShell then I thought to myself, there might just be a way. And I found it:
Import-Module C:\ps\StackOverflow\32743812\issue.psm1 -Force
function New-PSClassInstance {
param(
[Parameter(Mandatory)]
[String]$TypeName,
[object[]]$ArgumentList = $null
)
$ts = [System.AppDomain]::CurrentDomain.GetAssemblies() |
Where-Object Location -eq $null |
Foreach-Object {
$_.Gettypes()
} | Where-Object name -eq $TypeName |
Select-Object -Last 1
if($ts) {
[System.Activator]::CreateInstance($ts,$ArgumentList )
} else {
$typeException = New-Object TypeLoadException $TypeName
$typeException.Data.Add("ArgumentList",$ArgumentList)
throw $typeException
}
}
New-PSClassinstance -TypeName SxWindowsService
I assumed that using the script scope modifier on a function in a PowerShell module would prevent the function from being exported. Sample:
function script:Get-One { 1 }
When I import the module the Get-One function is exported.
Questions
Is it supposed to work to use the script scope modifier to make module functions private?
If not: Why? Any other scope modifiers that I can use?
I know I can use Export-ModuleMember to control which functions to export, but I only have a few functions that should not be exported. I would rather specify which functions to ignore.
script scope in a module is redundant because the default scope is the script/module in which it's defined. It is the equivalent of an instance variable. global in a module is akin to a static variable. Posh-Git for example uses a Global preferences variable for consistency between shells.
I used an in-memory module but the idea is the same when using a psd1 to define the arguments to New-Module
# remove module from namespace for repeated testing
if(Get-Module -Name 'SOTest') { Remove-Module -Name 'SOTest' }
new-Module -Function:'*' -Name:'SOTest' -ScriptBlock {
$global:helpers = [PSCustomObject]#{}
Add-Member -InputObject:$global:helpers -MemberType:ScriptMethod -Name:'HiddenFoo' -Value { return "Private Foo!" }
Add-Member -InputObject:$global:helpers -MemberType:ScriptMethod -Name:'HiddenFooWithArgs' -Value { return "Private " + $args -join ',' + '!' }
Add-Member -InputObject:$global:helpers -MemberType:ScriptMethod -Name:'HiddenFooWithParams' -Value { param ([string]$str, [int]$num); return "Private Str = $str + num; $num!" }
## script: scope refers to module scope, which makes it redundant
function WriteFoo {
Write-Output "PublicFoo!"
}
function WritePrivate {
#This fails, because the private now refers to the function scoped $private:helpers
$private:helpers.HiddenFoo | Write-Verbose -Verbose
}
function WritePrivateShort {
# This works because the helpers object is static across instances
$helpers.HiddenFoo() | Write-Verbose -Verbose
$helpers.HiddenFooWithArgs("Cow", "Moo") | Write-Verbose -Verbose
$helpers.HiddenFooWithParams("Four", 4) | Write-Verbose -Verbose
# This errors due to argument type mismatch
$helpers.HiddenFooWithParams("Five", "Five") | Write-Verbose -Verbose
}
} | Import-Module
# Prefer short errors for this demo
$ErrorView = "CategoryView"
Get-Module -Name 'SOTest'
'WF-----------------'
WriteFoo
'WP-----------------'
WritePrivate
'WPS----------------'
WritePrivateShort
'-------------------'
And the output:
ModuleType Version Name ExportedCommands
---------- ------- ---- ----------------
Script 0.0 SOTest {WriteFoo, WritePrivate, WritePrivateShort}
WF-----------------
PublicFoo!
WP-----------------
InvalidData: (:) [Write-Verbose], ParameterBindingValidationException
WPS----------------
VERBOSE: Private Foo!
VERBOSE: Private Cow Moo
VERBOSE: Private Str = Four + num = 4!
InvalidArgument: (:) [], RuntimeException
-------------------
For a Powershell module, Export-ModuleMember is the preferred way to do that.
As an alternative, you can define the function that you don't want exported from inside the function(s) which uses it (in the Begin block, for example).
This makes the function only visible by the "parent" function, making it effectively private.
Also, you could try using the Private scope , instead of the Script scope.
Does PowerShell call any initialization code when a module is loaded?
I am looking for something like a Perl BEGIN block, or a constructor.
Both NEW-MODULE and IMPORT-MODULE will return a PSCustomObject. I am trying to encapsulate a custom object in a module to avoid lengthy code in scripts. One method that tests well in open code is:
$m = new-module -scriptblock {
New-Object PSCustomObject |
Add-Member NoteProperty -name person -value Frodo -passthru |
Add-Member ScriptMethod Who { $this.person } -passthru |
Add-Member ScriptMethod Mod {
param($x)
$this.person = $x
} -passthru
} -ascustomobject -returnresult
Ideally I would like to drop this code into a module and use something like:
$MyObj = Import-Module -Name ".\MyPackage" -AsCustomObject
and have MyObj be a handle to an object the same as the first snippet provides.
Suggestions appreciated.
It's not clear if you want to run initialization code when a module is loaded (like Perl's BEGIN block) or if you want to create a custom class (which is what "constructor" suggests).
Initialization code in a module is easy. Any code in a module not embedded in a function is executed when the module is imported.
Creating a custom class isn't supported natively in PS. But see: http://psclass.codeplex.com/. You can also write C#, VBScript, etc. and use Add-Type.
Import-module won't work to simulate a class, because you can only have 1 instance of a module with a given name - at best you'd have a singleton class. (BTW, import-module does have a -passthru parameter, which would more or less make your last line of code work - as a singleton. You'd also have to add export-module -variable * -function * to your module code) You could use New-Module to simulate a class though. And you could wrap it in a function named, new-myClass for example.
BTW, if you use the -ASCustomObject parameter you end up with a hashtable, which doesn't support "this" (in words hash table values that are script blocks don't have a built-in way to refer to the hashtable itself). If you use new-module without -AsCustomObject (and potentially use a factory function, for example new-myclass) then you could simulate "this.varInMyModule" with & $myModule $varInMyModule. However if you create a PSCustomObject, using Add-Member, then script method have access to $this and it in general acts a lot more like a typical object with properties and methods.
Modules are really supposed to output cmdlets, not objects. A module should provide a set of related cmdlets. There is a way to send data into the module using Import-Modules's -ArgumentList parameter as show here. You could use the technique to provide a server name for your cmdlets to connect to for example. The PowerCLI module handles that differently using a cmdlet that creates a script scope connection object ($script:connection) that the other cmdlets check for and re-use if it exists similar to this:
#test.psm1
$script:myvar = "hi"
function Show-MyVar {Write-Host $script:myvar}
function Set-MyVar ($Value) {$script:myvar = $Value}
#end test.psm1
Using Modules you can export both innate properties and functions, and don't need to run them through add-member or do much acrobatics. Note however that it has some issues if you don't want to export all properties and methods, and while you can initialize properties to an initial value, you CAN'T call an internal function during initialization without doing some akward acrobatics.
But I think what you really want to do is use Classes which are now available in Powershell 5 (they weren't when you posted). I've provided examples of each.
Sysops has a decent tutorial on the new classes in 4 parts
Here's the older way before powershell 5.0
# powershell 3.0 and above (I think)
$m = new-module -ascustomobject -scriptblock `
{
$person = "Frodo"
function Who
()
{return $this.person}
function Rename
($name)
{$this.person = $name}
Export-ModuleMember -Function * -Variable *
}
write-host "direct property access: $($m.person)"
write-host "method call: $($m.who())"
$m.Rename("Shelob")
write-host "change test: $($m.who())"
Also you can replicate multiple objects from a template like this:
# powershell 3.0 and above (I think)
$template = `
{
$person = "Frodo"
function Who
()
{return $this.person}
function Rename
($name)
{$this.person = $name}
Export-ModuleMember -Function * -Variable *
}
$me = new-module -ascustomobject -scriptblock $template; $me.Rename("Shelob")
$you = new-module -ascustomobject -scriptblock $template
"Me: $($me.Who())"
"You: $($you.Who())"
And in powershell 5 you have actual classes (mostly)
#requires -version 5
Class Person
{
hidden [String] $name #not actually private
[string] Who ()
{return $this.name}
[void] Rename ([string] $name)
{$this.name = $name}
# constructors are weird though, you don't specify return type OR explicitly return value.
Person ([String]$name)
{$this.name = $name}
<#
# The above constructor code is secretly converted to this
[Person] New ([string]$name) #note the added return type and renamed to New
{
$this.name = $name
return $this #note that we are returning ourself, you can exploit this to create chained constructors like [person]::New("gandalf").withWizardLevel("White") but I haven't done so here
}
#>
}
$me = [Person]::new("Shelob")
$you = [Person]::new("Frodo")
# $me|gm # Note that Name doesn't show here
# $me.name # But we can still access it...
# $me|gm -Force # Note that Name DOES show here
"`n"
"Me: $($me.who())"
"You: $($you.who())"
$you.Rename("Dinner")
"You after we meet: $($you.who())"